vue.js
基础:
Vue构造器:Vue.extend(options)
- 是vue构造器的扩展,调用Vue.extend创建的是一个vue构造器。参数是一个包含组件选项的对象。
- options中的属性:
- template属性用于定义组件要渲染的html。在vue.extend()的选项中data必须是函数;
- 注意:调用vue.extend生成的是一个组件构造器,不能直接拿来渲染的,必须要使用vue.component来进行注册;
全局组件:Vue.component()
- 参数:2个:
- 第一个参数:组件的标签;
- 第二个参数是组件构造器;
- 组件应当挂载到某个vue实例上,否则不会生效;
Vue.component('组件名',{
// 选项(组件)
})
组件语法糖:
- 由于以上注册组件的过程过于繁杂,vue提供了注册语法糖:使用vue.component()直接创建和注册组件;
- 用法:vue.component(name, options)
- 参数:
- name: 组件标签名;
- options:选项对象
Vue.set(target,key,value)/this.$set(target,key,value)
- 应用场景:当生成VUE实例后,修改date中属性的值有时视图并不会同步更新,(即如果在实例创建后添加新的属性到实例上,他不会触发视图更新)
- 原因:Vue的双向绑定时基于set和get的
- 解决方法:使用Vue.set/this.$set来添加新属性;
- Vue.set(target,key,value)
- 参数:
- target: 要添加属性的对象(目标对象)
- key: 要添加的属性
- value:属性值
-eg:
<ul>
<li><span>正常类: {{earnings.a}}</span><span></span></li>
<li><span>宝宝类: {{earnings.b}}</span><span></span></li>
<li><span>活期存款: {{earnings.c}}</span><span></span></li>
</ul>
<button @click="getNum"></button>
export default {
data () {
return {
earnings: {},
}
},
methods: {
getNum () {
this.earnings.a = 1
this.earnings.b = 2
this.earnings.c = 3
}
}
// 1.如上点击button按钮,视图不会更新;
// 2.将getNum函数改写成如下形式,即可式对象earnings的属性变成响应式的
getNum () {
this.$set(earnings, 'a', 1)
this.$set(earnings, 'b', 2)
this.$set(earnings, 'c', 3)
}
this.$nextTick([callback, context])/Vue.nextTick([callback, context])
- 应用场景:有时我们通过更改数据 配合v-html更新dom,然后获取更新后的dom,但 数据更新后dom不会马上更新完成,此时就应用到了this.$nextTick;
- 作用:在下次Dom更新循环结束之后执行回掉,在数据更新之后立即调用该方法,获取更新后的Dom;
理解:
- vue.js是一个构建数据驱动的web界面的库。Vue.js的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件
- vue.js自身并不是一个全能框架,它只聚焦于视图层。因此学习起来比较容易,它也非常容易和其他库已有项目整合。另一方面,在与相关工具和支持库一起使用市,vue.js也能完美驱动复杂的但也应用。
- vue.js的核心是一个响应的数据绑定系统,它让数据与Dom保持同步非常简单。在使用jquery手动操作DOM时,我们的代码常常是命令式的,重复的与易错的,vue.js用数据驱动的视图的概念,通俗的讲,将意味着我们在普通的HTML模板中使用特殊的语法将DOM‘绑定’到数据层,一旦创建了绑定,DOM将和数据保持同步。每当修改了数据,DOM便相应的更新。这样我们应用中的逻辑几乎都是修改数据了,比与DOM更新搅在一起。这让我们的代码更容易撰写,理解与维护
vue.js中使用到的两个重要函数。
- Object.defineProperty(object, propertyname, decriptor)
- eg: 添加属性访问器
var obj = {},newVal;
// Add an accessor property to the object.
Object.defineProperty(obj, "name", {
set: function (x) {
console.log("in property set accessor" + newVal);
this.firstName= x;
},
get: function () {
console.log("in property get accessor" + newVal);
return this.firstName;
},
enumerable: true,
configurable: true
});
vue和angular的
vuex严格模式
开启严格模式;
const store = new Vuex.Store({
// ...
strict: true
})
JSlint语法要求
- 代码文档结尾要留白一行;
- 一句代码结尾不能用 ‘;’,不能有空格
- 注释格式‘空格 // 注释内容’
严格模式中this的指向
- 严格模式中自调用函数this不指向window,指向undefined
bind
和apply, call作用类似,只是bind不会改变原函数的this指向,他会返回一个新的函数,改变新函数的this指向为指定的值,新函数的函数体和原函数一样
eg
this.x = 9;
var obj = {
x: 81,
getX: function() { return this.x; }
};
obj.getX(); // 返回 81
var retrieveX = obj.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(obj);
boundGetX(); // 返回 81
路由懒加载;
- 使用原因:使用vue写单页面应用时,会出现打包后javascript包非常的大,影响页面加载,可以用路由懒加载的方法去优化路由,当用到某个路由时在去加载对应的模块;
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: resolve => require(['components/Hello.vue'], resolve)
},
{
path: '/about',
component: resolve => require(['components/About.vue'], resolve)
}
]
})
vue中watch
- 用来添加监视,其值为一个对象,要观察的数据以键值的形式写入其中,(一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。)
- 键为要观察的数据,值为一个函数表达式,函数表达式有两个参数;第一个为新值,第二个为老值(变化前的值),
- 当药观察的数据发生改变时,会执行对应键的函数表达式;
element-ui
- 模态框可以通过 v-model来呼出,取值为boolean
es6向外暴露模块的方法;
- export
- export { 要暴露的变量名 }
- export var/ const/let 变量名 = 对象/function
- export default = obj
- module exports = object/function
eg:
1.第一种方法:
const form = {
id: input().protect(true).value(),
name: RI().label('姓名').val('').type('text').checkDefault().value(),
}
export { form } // 向外暴露一个form对象
app.use()
- 参数:obj|function
- 安装vue.js插件式如果插件是一个对象,必须提供install方法,如果插件是一个函数,那么它会被作为install方法,install方法将被作为vue的参数;
vue的特殊属性
slot :内容分发插槽
- 具名插槽/和匿名插槽: 可以使用元素的一个特殊属性name来配置如何分发内容,将给据name属性来进行内容匹配,可以使用一个匿名插槽作为默认插槽,负责匹配不到插槽的能容将被抛弃。
- eg:
- 假定有一个app-layout组件,其模板为:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
- 父组件模板:
<app-layout>
<h1 slot="header">这里可能是一个页面标题</h1>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
<p slot="footer">这里有一些联系信息</p>
</app-layout>
- 选人结果为:
<div class="container">
<header>
<h1>这里可能是一个页面标题</h1>
</header>
<main>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
</main>
<footer>
<p>这里有一些联系信息</p>
</footer>
</div>
- 注意:如果子组件中没有匿名插槽,那么匹配不到插槽的那部分内容将被抛弃
计算属性
- 使用计算属性的原因:模板内表达式是非常便利的,但是他们实际是用于简单运算的,在模板中放入太多逻辑会让模板过重且难以维护。
- eg:
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// a computed getter
reversedMessage: function () {
// `this` points to the vm instance
return this.message.split('').reverse().join('')
}
}
})
- 计算属性(computed)VS被观察的属性
- 当某一个值A依赖于另外两个值BC且,当BC中任意一个值发生改变是A的值也要发生改变,此时使用计算属性,比watch更简洁。
- eg:
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
//上面代码时命令式的和重复的,使用计算属性书写如下,会变得简洁明了
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
- 何时使用watch?
- 当你想要在数据变化响应时,执行一步操作,或者开销较大的操作时使用。
ES6新增的数组方法:
- reduce( function(accumulator, currentValue, currentIndex, array), initialValue) :为数组中的每一个元素一次执行回掉函数,不包括数组中被删除的或者从未被赋值的元素
- 参数:
- callback :执行数组中每个值的函数
- 参数四个:accumulator:上一次 回掉的返回值
- currentValue:正在处理的元素
- currentIndex:正在处理的元素的索引
- array: 调用reduce的数组
- initialValue :可选项,气质用于地磁调用callback的第一个参数,如果没有设置初始值,则将数组中的第一个元素作为初始值,空数组调用reduce
- callback :执行数组中每个值的函数
总结:数组中的元素每次执行callbak函数会将return的值赋给accumulator
webpack config文件配置
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin'); //css单独打包
var HtmlWebpackPlugin = require('html-webpack-plugin'); //生成html
var ROOT_PATH = path.resolve(__dirname);
var APP_PATH = path.resolve(ROOT_PATH, 'src'); //__dirname 中的src目录,以此类推
var APP_FILE = path.resolve(APP_PATH, 'App.jsx'); //根目录文件app.jsx地址
var BUILD_PATH = path.resolve(ROOT_PATH, '/pxq/dist'); //发布文件所存放的目录
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: {
app: APP_FILE
},
output: {
publicPath: '/pxq/dist/', //编译好的文件,在服务器的路径,这是静态资源引用路径
path: BUILD_PATH, //编译到当前目录
filename: '[name].js', //编译后的文件名字
chunkFilename: '[name].[chunkhash:5].min.js',
},
module: {
loaders: [{
test: /\.js$/,
exclude: /^node_modules$/,
loader: 'babel',
include: [APP_PATH]
}, {
test: /\.css$/,
exclude: /^node_modules$/,
loader: ExtractTextPlugin.extract('style', ['css', 'autoprefixer']),
include: [APP_PATH]
}, {
test: /\.less$/,
exclude: /^node_modules$/,
loader: ExtractTextPlugin.extract('style', ['css', 'autoprefixer', 'less']),
include: [APP_PATH]
}, {
test: /\.scss$/,
exclude: /^node_modules$/,
loader: ExtractTextPlugin.extract('style', ['css', 'autoprefixer', 'sass']),
include: [APP_PATH]
}, {
test: /\.(eot|woff|svg|ttf|woff2|gif|appcache)(\?|$)/,
exclude: /^node_modules$/,
loader: 'file-loader?name=[name].[ext]',
include: [APP_PATH]
}, {
test: /\.(png|jpg)$/,
exclude: /^node_modules$/,
loader: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]',
//注意后面那个limit的参数,当你图片大小小于这个限制的时候,会自动启用base64编码图片
include: [APP_PATH]
}, {
test: /\.jsx$/,
exclude: /^node_modules$/,
loaders: ['jsx', 'babel'],
include: [APP_PATH]
}]
},
plugins: [
new htmlwebpackPlugin({ // 生成html文件配置;
// 生成html文件的名字,路径和生产环境下的不同,要与修改后的publickPath相结合,否则开启服务器后页面空白
filename: 'index.html',
// 源文件,路径相对于本文件所在的位置
template: path.resolve(__dirname, 'index.html'),
// 需要引入entry里面的哪几个入口,如果entry里有公共模块,记住一定要引入
// chunks: ['vendors', 'index'],
// 要把<script>标签插入到页面哪个标签里(body|true|head|false)
// inject: 'body',
// 生成html文件的标题
// title: ''
// hash如果为true,将添加hash到所有包含的脚本和css文件,对于解除cache很有用
// minify用于压缩html文件,其中的removeComments:true用于移除html中的注释,collapseWhitespace:true用于删除空白符与换行符
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin() // 错误提示;
],
resolve: {
extensions: ['.js', '.vue', '.json'],
alias:{
// 'vue$': 'vue/dist/vue'
}
}
}
- 配置文件解释:
- loader 配置中的include/exclude:
- include 表示哪些目录中的文件需要进行打包
- exclude 表示哪些目录中的 .js 文件不要进行那些目录中的文件不需要进行打包
vue.use
- 安装vue.js的插件,如果插件是一个对象,必须提供install方法,如果插件是一个函数,他会被作为install方法。install方法将会被vue参数调用;当一个install方法被一个vue插件多次调用,他只会被安装一次
VUE注意点:
- vue-router
- 注意vue-router的参数:为一个对象{routes: 路由配置对象组成的数组}
- 固定写法:
Vue.use(VueRouter)
let router = new VueRouter({
routes: routers
})
new Vue({
el: '#app',
router
})
vuex:
- 是一个专为Vue.js开发的状态管理模式 。它采用集中式存储管理应用的所有组件状态,并以相应的规则保证状态以一种可预测的方式发展变化。
- 状态管理模式分为一下机部分:
- state, 驱动应用的数据源;
- view, 已声明方式将state映射到视图;
- actions, 响应在view上的用户输入导致的状态变化。
- 什么时候回用到vuex?
当我们遇到多个组件共享状态是,单向数据流的简洁性很容易遭到破坏:(1,多个视图依赖于同一状态; 2, 来自不同行为的视图需要变更同一状态。)
我们经常会采用子组件直接引用或通过事件来变更来同步组状态的多份拷贝
总结:此时我们会把组件的共享状态抽取出来,以一个全局的单列模式进行管理。
- 安装 :vuex属于第三方插件,毫无疑问要使用Vue.use()进行注册安装,通过在根实例中注册store选项,改store实例会注入到根组件下的所有子组件中,在子组件中通过this.$store可以访问到store实例
eg:
let store = new Vuex.Store({ // 注意Store要大写
state:{
count: 0
},
mutations: {
add(state){
state.count++
}
}
})
new Vue({
el: '#app',
router,
store //在根实例中通过store选项注册
})
- 核心概念:
- state: 单一状态树:vue用一个对象包含了全部应用的层级状态, 至此它便作为一个“唯一的一个数据源(SSOT)”而存在。而这也意味着每个应用将仅仅只包含一个store实例。单一状态树让我们能够直接地定位任意特定的状态片段,在调试过程中也能轻而易举地取得整个当前应用的状态快照。
get/set
-
get语法将一个对象属性绑定到查询该属性时将被调用的一个函数上; 被绑定的函数必须不带参数。
- 语法:
{get prop (){...}} {get [expression](){...}}
- 参数:prop:要绑定到给定函数的属性名
- 从ECMAScript 2015 (ES6)开始,还可以使用一个计算的属性名的表达式绑定到给定的函数
set 将对象属性绑定到要调用的一个函数上,当尝试设置该属性时调用函数;被绑定的函数必须有一个明确的参数
-语法:
{set prop(val) {....}}
{set [expression](val) { . . . }}
- 参数:
- prop :要绑定到给定函数的属性名。
- val: 用于保存尝试分配给prop的值得变量的一个别名
- expression: 从ECMAScript 2015 (ES6)开始,还可以使用一个计算的属性名的表达式绑定到给定的函数。
脏检查原理分析:
- $watch对象: Angular每一个绑定到UI的数据,就会有一个$watch对象,这个对象包含三个参数:
watch = {
name: '' // 当前watch对象观察的数据名
getNewValue: function($scope){
....
return newValue;
},
listener: function(newValue, oldValue){ // 当数据发生改变是需要执行的操作。
.....
}
}
getNewValue()可以得到当前$scope上的最新值,listener函数得到新值和旧值并进行一些操作。
我们在使用angular的时候,listener一般都为空,只有当我们需要检测更改事件的时候,才会显示的添加监听。
每当我们将数据绑定到UI上,angular就会向你的watchList上插入一个$watch
- 脏检查:浏览器并没有提供数据检测的API,故而内存的任何数据变动(UI事件,ajax请求,timeout等回调操作导致的数据变动)都无法被Listener,但我们可以基于这些大部分能够产生数据变动的事件进行封装(如:click, mouseenter, Timeout,....),在每次事件发生后,执行完事件后,检查一遍数据的变化,如果数据和上次的值有变化,则执行这个值(注册时)对应的callback(框架中),这个callback可能是view层的一个数据表现,也可能是一段处理function;同时在检查的时候,由于你并不知道这个事件是对那些数据进行了更改,以及这个事件可能造成事件之外其他任何地方的数据更改,所以必须进行一次大检查,将所有注册过的值全部检查一遍,一次检查称为一个周期,每次检查至少两遍,第二遍用来确认前一遍变动中是否有数据变动导致了其他数据的变动,如果第二次有变动的话,会才执行一遍,知道最后两次完全一致,才停止检查。
即遍历watchList
Object的属性和方法
object.defineProperty() :在对象上定义一个新属性,或者修改对象现有的属性,并返回这个对象。
- 语法:
Object.defineProperty(obj, prop, descriptor)
-
参数:
- obj: 需要被操作的目标对象。
- prop: 目标对象需要定义或者修改的属性名称。
- descriptor: 将被定义的属性的描述符,其取值如下:
- configurable :当且仅当改属性的configurable为true时,改属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为false,
- 当且仅当改属性的enumerable为true时,该属性才能出现在对象的枚举属性中。默认为false
- value: 该属性对应的值。可以是任何有效的javascript值(数值,对象 ,函数等)。默认为undefined.
- 当且晋档该属性的write值true时,该属性才能被赋值运算符改变。默认为false.
- get : 一个给属性提供getter的方法,如果没有getter则为undefined.改方法返回值被用作属性值。默认为undefined.
- set: 一个给属性提供setter的方法,如果没有setter则为undefined.该方法接受唯一个参数,并将改参数的新值分配给改属性。默认为undefined.
返回值: 被传递给函数的对象;
vue-cli 生成项目:
- 安装vue-cli :npm
- vue init webpack 项目名称
- eslint规则(默认):
- no-unused-expressions:1 (不允许短路求值,不允许三目运算),要在eslint配置文件中手动加1更改成0
- 脚手架生成的项目默认是不能打包sass文件的,要想打包sass文件需需执行以下步骤:
- 安装node-sass,和sass-loader模块
- 在webpack.base.conf.js文件添加一条打包规则:
{
test: /\.scss$/,
include: '/src/',
loaders: ['style-loader','css-loader','sass-loader']
}
-
注意不要动 utils.js中的如下配置,就默认是scss语法
git常用指令:
git 新建项目流程:
- 创建远程仓库;
- 在本地创建一个文件夹,
- 初始化本地仓库:git init
- 添加工作区的文件到暂存区:git add .
- 连接远程仓库:git remote add origin 远程仓库地址
- 取回远程仓库的变化并与本地分支合并:git pull origin master
- 提交暂存区代码到本地仓库区:git commit -m '注释'
- 提交本地仓库代码到远程仓库:git push origin master
- 将本地创建的新分支并切换到改分支:git checkout -b 新分支名
- 将本地创建的新分支推送的远程服务器:git push --set-upstream origin 分支名
特殊指令汇总
- 将远程仓库里制定分支拉去到本地(本地尚不存咋该分支):
- git checkout -b 要拉取的分支 origin/要拉取的分支
- 拉去成功会在本地生成一个新分支并并自动切换到改分支上,若出现如下提示:
则先要执行: git fetch ,然后在执行上一步的命令;fatal: Cannot update paths and switch to branch 'dev2' at the same time. Did you intend to checkout 'origin/dev2' which can not be resolved as commit?
- 强制push(一般仅用与初次创建项目时):
- git push -u origin master -f
axios:
- axios不是vue的第三方插件,所以不能使用vue.use()来注册,要利用prototype属性,将它添加到vue的原型中。vue.prototype = axios
360浏览器帮客户控制内核的方法:
若页面需默认用极速核,增加标签:<meta name="renderer" content="webkit"/>
若页面需默认用ie兼容内核,增加标签:<meta name="renderer" content="ie-comp"/>
若页面需默认用ie标准内核,增加标签:<meta name="renderer" content="ie-stand"/>
js中的继承:
- 通过原型链继承:
function fun1(){
this.name = '小猪'
}
fun1.prototype.getAge = function(){
alert(this.name)
}
function fun2(){
this.name = '皮皮鳝'
}
fun2.prototype = new fun1()
// 实现了fun2继承fun1
- 通过class 来继承:class可以通过extends关键字来实现继承。
Http协议与TCP区别:
TCP协议对应于传输层,儿HTTP协议对应于应用层,从本质上来说,而这没有可比性。Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次HTTP请求。http请求会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这过程非常短暂。所以HTTP连接是一种短连接,是一种无状态的连接。所谓的无状态,是指浏览器每次向服务器发起请求的时候,不是通过同一个连接,而是每次都建立一个新的连接。每次请求结束后,连接关闭,相关内容就释放了,所以记不住任何状态,成为了无状态连接。
-
keep-Alive
- 出现原因:如今的网页变的复杂了,里面可能会有很多的图片,每次访问图片都需要建立一个tcp连接就显得极其低效。
作用:从HTTP/1.1起,默认开启了Keep-Alive,保持连接的特性(当一个网页打开完成后,客户端与服务端用于传输HTTP数据的TCP连接不会立即关闭,会保持一段时间,可以在服务器软件中设定这个时间。引为这个时间范围是有限的,所以仍然将HTTP协议看做是无状态的。所以出现了Session、Cookie等相关技术,来保持用户的一些状态。)
http是无状态的短连接,TCP是有状态的长连接,虽然http是建立在TCP的基础上的,但HTTP每次请求完成就把TCP连接关了,所以是短连接。而我们通过Socket编程使用TCP协议的时候,因为我们自己可以通过代码去控制什么时候打开连接,什么时候关闭连接,只要我们不通过代码把连接关闭,这个连接就会在客户端可服务端的进程中一只存在,相关状态数据会一直保存着。
js中检测对象中是否存在某个属性。
- in 关键字:
var o={x:1};
"x" in o; //true,自有属性存在
"y" in o; //false
"toString" in o; //true,是一个继承属性
- hasOwnProperty():
该方法只能判断自有属性是否存在,对于继承的属性(原型propto中的属性)会返回false。
vue中路由传参$prams和query的区别:
params:/router1/:id ,/router1/123,/router1/789 ,这里的id叫做params
query:/router1?id=123 ,/router1?id=456 ,这里的id叫做query。
路由设置这里,当你使用params方法传参的时候,要在路由后面加参数名,并且传参的时候,参数名要跟路由后面设置的参数名对应。使用query方法,就没有这种限制,直接在跳转里面用就可以。
eg:如下两种写法使用this.$route中params属性和query的区别:
1:params方式传参
路由配置:
地址栏:
打印this.$route:
2:query方式传参
路由配置:
地址栏:
打印this.$route:
Vue与Angular的比较,Vue的单向数据流注意点
字符串的一些方法:
-
replace
- 语法:stringObject.replace(regexp/substr,replacement)
-
参数:
- regexp/substr:规定字符串或要替换的RegExp对象。
replacement : 规定了替换文本或者生成替换文本的函数
当replacement为函数时:函数有两个参数一个是为匹配到的字符串,第二个为字符串的索引(将匹配到的字符串看成是一个整体)
let a = 'adfjdjfsdjf{04}sdjfasdflsdj{143}'.replace(/\{[0-9]*\}/g, function (a, b) {
console.log(a, '----', b);
return '---'
})
打印结果为:
{04} ---- 11
{143} ---- 27