第3章

Vue.js基本语法





万丈高楼平地起。本章主要从如下几方面,介绍Vue.js的基本语法。
1. Vue.js对象
介绍如何创建Vue.js对象,如何在Vue.js对象中定义数据属性和方法,以及Vue.js对象的生命周期。
2. 插值表达式
介绍如何在视图上绑定Vue.js对象的数据属性,包括简单文本的绑定、html值的绑定、元素属性值的绑定、样式值的绑定和JavaScript表达式的值绑定等。
3. 表单输入绑定
介绍如何给复杂的表单元素绑定动态的数据。
4. 事件处理
介绍如何给视图元素的事件绑定事件监听,事件触发后将执行绑定的函数。
5. 指令
介绍Vue.js提供的常用指令,实现页面中事件的绑定、元素属性值的绑定和显示内容的逻辑控制指令等。
6. Vue.js响应原理
介绍Vue.js框架内部的响应原理及对对象、数组的检测响应,还有异步更新问题的处理。
3.1Vue.js对象
虽然从严格意义上讲,Vue.js没有完全遵循MVVM模型,但是Vue.js的整体设计还是受到了MVVM模型的启发,在Vue.js中有个非常重要的概念——Vue.js实例。在后面的章节中,会经常使用VM(ViewModel的缩写)表示Vue.js实例。
每个Vue.js应用都是通过Vue.js函数创建一个新的Vue.js实例开始的,而且每个Vue.js应用都是由通过Vue.js函数创建的一个根Vue.js实例,以及多个可选的、嵌套的、可复用的组件树组成,如一个Todo应用的组件树结构如图3.1所示。


图3.1Todo应用组件树


创建Vue.js实例的语法代码如下: 



var vm = new Vue({

//选项

})








注意语法中的Vue.js,它是定义在Vue.js中的函数,所以在使用上面的语法创建Vue.js实例之前,需要引入Vue.js,代码如下: 



<script type="text/JavaScript" src="../static/js/Vue.js"></script>




3.1.1Vue.js实例的数据属性
创建Vue.js实例的时候,可以在创建的JSON对象中,使用可选属性data定义Vue.js实例的property属性,这些property属性将会被加入Vue.js的响应式系统中。当这些property属性的值发生改变的时候,视图将会产生“响应”,即匹配后更新为新值,代码如下: 



//第3章/创建Vue.js实例和定义data属性.html



//数据对象

var data = { a: 1 }



//该对象被加入一个 Vue.js实例中

var vm = new Vue({

data: data

})



//获得这个实例上的 property

//返回源数据中对应的字段

vm.a == data.a //=> true



//设置 property 也会影响原始数据

vm.a = 2

data.a //=> 2







//反之亦然

data.a = 3

vm.a //=> 3




当这些数据改变时,视图会进行重新渲染。值得注意的是,只有当实例被创建时就已经存在于data中的property才是响应式的。也就是说,如果添加一个新的property,例如vm.b='hi',则对b的改动将不会触发任何视图的更新。如果开发人员知道在晚些时候需要一个或多个property属性,则需要在data属性中定义这些property属性,给它们赋予空值,后面需要的时候再操作或使用这些property,代码如下: 



data: {

newTodoText: '',

visitCount: 0,

hideCompletedTodos: false,

todos: [],

error: null

}




这里唯一的例外是使用 Object.freeze()函数会阻止修改现有的property属性的“响应”,也意味着响应系统无法再追踪变化。因为调用了Object.freeze()函数,改变property属性值后,不会渲染到视图上,代码如下: 



//第3章/freeze方法.html



var data = {

foo: 'bar'

}

//阻止property属性的响应

Object.freeze(data)



const vm = new Vue({

el: '#app',

data: data

})

<div id="app">

<p>{{ foo }}</p>

<!-- 这里的 `foo` 不会更新! -->

<button v-on:click="foo = 'baz'">Change it</button>

</div>




除了数据 property属性,Vue.js实例还暴露了一些有用的property属性。开发人员可以在这些property属性名前面添加
$前缀,以便与开发人员定义的property区分开来,代码如下: 



//第3章/freeze方法.html

var data = {

foo: 'bar'

}

var vm = new Vue({

el: '#app',

data: data

})



vm.$data === data //=> true

vm.$el === document.getElementById("app") //=> true




关于Vue.js实例自己暴露的其他property属性,读者可以参考官网(https://cn.vuejs.org/v2/api/)。
3.1.2Vue.js实例的方法
开发者可以在创建Vue.js实例对象的时候,在传入的JSON可选项中自定义若干方法封装业务逻辑,以便于在需要的时候重复调用,代码如下: 



//第3章/自定义Vue.js实例方法.html

<div id="app">

{{count}}

<br/>

<!--给单击事件绑定increment方法-->

<input type="button" v-on:click="increment" value="单击递增"></input>

</div>

<script type="text/JavaScript">

const data = {count:0}

const vm = new Vue({

el:'#app',

data,

methods:{

increment){

this.count++

}

}

})

//调用两次vm实例中的increment方法

vm.increment()

vm.increment()

</script>




如上面代码所示,开发人员可以在传给Vue.js函数的JSON对象参数中用methods属性定义多个自定义方法,在上面的样例代码中定义的是increment方法。定义好后,可以将它们绑定到页面元素的事件上,对应事件触发后自动执行,也可以在JavaScript代码中,同调用普通方法的语法一样调用。
同Vue.js实例的数据属性一样,开发人员可以自定义多种方法,也可以使用Vue.js实例
暴露的其他方法,在使用的时候,也是用$作为前缀,用以区分开发人员的自定义方法。如vm.$mount方法可以实现将vm对象挂载到指定的DOM元素上。在这里对这些方法不进行详细介绍,后面章节会陆续介绍常用的方法。读者可以参考官网(https://cn.vuejs.org/v2/api/),也可以参考附录API。
3.1.3Vue.js实例生命周期
每个Vue.js实例在被创建时都要经过一系列的初始化过程,例如,需要设置数据监听、编译模板、将实例挂载到DOM并在数据变化时更新DOM等。同时在这个过程中也会运行一些叫作生命周期钩子的函数,这给了开发人员在不同阶段添加自己代码的机会。
Vue.js实例从创建到销毁的整个过程分为创建、绑定、更新和销毁4个阶段。对于每个阶段,Vue.js里面都定义了两个对应的钩子函数,它们分别是beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。
从调用Vue.js的构造函数开始,Vue.js实例的生命周期的详细说明如下。
(1) 创建Vue.js实例。
(2) 初始化实例的事件和生命周期钩子函数。
(3) 调用beforeCreate钩子函数。
(4) 初始化Vue.js实例的注入属性和方法。
(5) 调用created钩子函数。
(6) 判断是否设置el选项。如果没有,就不将vm对象挂载到DOM元素上,否则进入下一步。
(7) 判断是否有el对应的模板。如果有对应的模板,就将模板转换成render函数,通过render函数完成渲染; 如果没有对应的模板,就将el对应的外层html作为模板,进行渲染。
(8) 调用beforeMount钩子函数。
(9) 在渲染的视图上,绑定vm中定义的数据。
(10) 调用mounted钩子函数。
(11) 当vm中的数据发生变化时,循环地调用beforeUpdate钩子函数,更新页面显示,调用updated钩子函数。
(12) 在销毁Vue.js实例之前,调用beforeDestroy钩子函数。
(13) 清除Vue.js实例上的所有监听器子组件等。
(14) 调用destroyed,完成Vue.js实例的销毁。
开发人员可以通过在代码中实现beforeCreate、created、beforeMount、mounted、beforeUpdate、
updated、beforeDestroy和destroyed钩子函数,将合适的业务功能植入Vue.js实例的对应生命周期阶段执行。
Vue.js实例的完整生命周期流程可以参考图3.2。生命周期钩子方法的定义可以参考的代码如下: 



图3.2Vue.js实例生命周期





//第3章/Vue.js实例生命周期.html

<!DOCTYPE html>

<html lang="en">

<head>

…

<script type="text/JavaScript" src="../static/js/Vue.js"></script>

</head>

<style type="text/css"></style>

<body>

<div id='app'>

{{message}}

</div>

<script type='text/JavaScript'>

let vm = new Vue({

el: '#app',

data: {

message: 'old message'

},

methods: {

},

beforeCreate: function(){

console.log('beforeCreate')

},

created: function(){

console.log('created')

},

beforeMount: function(){

console.log('beforeMount')

},

mounted: function(){

console.log('mounted')

},

beforeUpdate: function(){

console.log('beforeUpdate')

},

updated: function(){

console.log('updated')

},

beforeDestroy: function(){

console.log('beforeDestroy')

},

destroyed: function(){

console.log('destroyed')

}

})

vm.$data.message = 'new message'







setTimeout(()=>{

const appObject = document.getElementById("app";

document.body.removeChild(appObject)

vm = null

}, 2000);



</script>

</body>

</html>





3.2插值表达式
插值表达式的作用是在页面的对应位置插入动态的数据,以便动态地显示。当被插入的数据对象在业务逻辑中发生改变时,会实时地更新到页面上。根据插入动态值的不同位置,分为文本插值、原始html插值、JavaScript插值、class属性插值和内嵌样式插值5种插入表达式,它们的语法和使用时的注意事项如下。
1. 文本插值表达式
文本插值表达式的作用是在标签之间插入动态的文本值。
语法如下: 



{{表达式}}




案例代码如下: 



<span>hello, {{userName}}</span>




说明:  在span元素中,动态显示userName数据对象的值。以文本的形式显示userName的值。如果userName="zhangsan",则显示“hello,张三”; 如果userName=<b>zhangsan</b>,则显示hello,<b>zhangsan</b>。

2. 原始html插值表达式
原始html插值表达式的作用是在标签之间插入原始的html内容。如果里面包含html标签,则浏览器会进行解析。
语法如下: 



<标签 v-html='表达式'></标签>




案例代码如下: 



<p>显示html内容 <span v-html='userName'></span></p>




说明:  
如果userName的值是<font color='red'>zhangsan</font>,则显示的结果为 显示红色的“zhangsan”。

3. 属性插值表达式
属性插值表达式的作用是给html元素的属性绑定动态值。特别需要注意的是,Mustache语法(双大括号语法)是不能用在html元素属性上的。
语法如下: 



<标签 v-bind:属性名称='表达式'>...<标签>




案例代码如下: 



<div v-bind:id="divId">div demo</div>




说明:  
对于布尔属性(只要存在就意味着其值为true),vbind 工作起来略有不同。示例代码如下: 



<button v-bind:disabled="isButtonDisabled">Button</button>




如果isButtonDisabled的值是null、undefined或false,则disabled属性不会被包含在渲染出来的<button>元素中,只有当isButtonDisabled是true或在JavaScript中能转换成true时,才会被渲染出来。

4. class属性插值表达式
class是html标签中的一个特殊属性,给class属性绑定动态值也采用vbind的方式,同普通属性一样。将active数据属性的值绑定到div的class属性上,代码如下: 



<div v-bind:class="active">..</div>




对于class属性,还支持根据条件绑定数据,将多个类样式和新旧class值一起绑定,代码如下: 



<div v-bind:class="isActive:active">..</div>




其中 isActive和active都是定义在Vue model中的属性名称,只有当isActive的值(或转换后的值)为true时,才将active属性的值绑定到class属性上,否则不绑定,从而实现根据条件给class属性绑定值,代码如下: 



<div v-bind:class="{clsName1, clsName2 ...}">..</div>




其中clsName1和clsName2都是Vue model的属性名称,Vue.js的渲染结果是同时给div的class属性绑定clsName1和clsName2对应的两个类样式。当然,也可以给每个clsName添加条件控制,代码如下: 



<div v-bind:class="{isClsName1:clsName1, clsName2...}">..</div>




最后,div自己有个class属性,其值是static,同时用vbind: class给class属性绑定了clsName动态属性值,代码如下: 



<div class="static" v-bind:class="clsName">..</div>




渲染结果是最后的html的class属性值既包含static,也包含clsName对应的动态值,代码如下: 



<div class="static clsValue">..</div>




5. 内嵌样式插值表达式
vbind: style的对象语法十分直观——看着非常像CSS,但其实是一个JavaScript对象。CSS property名可以用驼峰式(camelCase)或短横线分隔(kebabcase,记得用引号括起来)来命名,代码如下: 



<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>



data: {

activeColor: 'red',

fontSize: 30

}




直接绑定到一个样式对象通常更好,这会让模板更清晰,代码如下: 



<div v-bind:style="styleObject"></div>



data: {

styleObject: {

color: 'red',

fontSize: '13px'

}

}




vbind: style的数组语法可以将多个样式对象应用到同一个元素上,代码如下: 



<div v-bind:style="[baseStyles, overridingStyles]"></div>




6. JavaScript插值表达式
在页面中除了可以插入一个简单的属性值外,也可以绑定JavaScript表达式,代码如下: 



{{userName+' demo'}} 

{{ok?'yes':'no'}} 

{{message.split('').reverse().join('')}}

<div v-html="'hello' + userName"></div>




这些表达式会在所属Vue.js实例的数据作用域下作为 JavaScript 被解析,但有个限制,即每个绑定都只能包含单个表达式,所以下面的例子都不会生效,代码如下: 



<!-- 这是语句,不是表达式 --> 

{{ var a = 1 }} `

<!-- 流控制也不会生效,请使用三元表达式 --> 

{{ if (ok) { return message } }}




注意,模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date。不能在模板表达式中试图访问用户定义的全局变量,全局变量的白名单如下: 



const allowedGlobals = makeMap(

'Infinity,undefined,NaN,isFinite,isNaN,' +

'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI, '+

'encodeURIComponent,Math,Number,Date,Array,Object,Boolean,'+

'String,RegExp,Map,Set,JSON,Intl,require' //for Webpack/Browserify

)




3.3表单输入绑定
开发人员可以用 vmodel 指令在表单元素<input>、<textarea>及<select> 上创建双向数据绑定。vmodel会根据控件类型自动选取正确的方法来更新元素。虽然看起来有些神奇,但vmodel本质上不过是语法糖(Syntactic Sugar)。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
vmodel会忽略所有表单元素的value、checked、selected attribute的初始值而总是将Vue.js实例的数据作为数据来源。应该通过JavaScript在组件的data选项中声明初始值。
vmodel在内部为不同的输入元素使用不同的property并抛出不同的事件。
(1) txt和textarea元素使用value property和input事件。
(2) checkbox和radio元素使用checked property和change事件。
(3) select元素将value作为prop并将change作为事件。
下面从vmodel的基本使用、值绑定和修饰符3方面,对使用vmodel实现表单输入绑定进行介绍。
3.3.1基本用法
在项目开发过程中,经常希望在表单元素中输入值后,能自动同步到对象属性中。vmodel很好地解决了这个问题。下面通过案例,分别演示使用vmodel改变表单元素的值后,如何自动同步到对象属性中。
1. 单行文本输入框
在单行文本输入框input type="text"元素中,用vmodel绑定了Vue.js实例中的message数据属性。当用户在单行文本输入框中输入值时,会自动被同步到message属性中,从而同步div中message的显示内容,代码如下: 



//第3章/表单输入绑定之基本用法.html

…



<div id='app'>

<!--单行文本输入框-->

<input type="text" v-model="message" placeholder="输入值"/><br/>



<div>

message is: {{message}}

</div>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

message: ''

},

methods: {

}

})

</script>

…




2. 多行文本输入框
在多行文本输入框textarea元素中,用vmodel绑定了Vue.js实例中的message数据属性。当用户在多行文本输入框中输入值时,会自动被同步到message属性中,从而同步div中message的显示内容,代码如下: 



//第3章/表单输入绑定之基本用法.html

…



<div id='app'>

<!—多行文本输入框-->

<textarea v-model="message" placeholder="输入值"></textarea><br/>



<div>

message is: {{message}}






</div>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',


data: {

message: ''

},

methods: {

}

})

</script>

…




使用textarea元素时需要注意,在文本区域插值(<textarea>{{text}}</textarea>)不会生效,需要用vmodel来代替。
3. 复选框
复选框绑定的对象属性值可以是boolean类型的值,也可以是其他值。绑定的对象属性是boolean类型的值,当checkbox被选中时,会被同步到checked数据属性,这个属性是boolean类型的,同时会在label部分显示true,否则显示false,代码如下: 



//第3章/表单输入绑定之基本用法.html

…

<!--复选框绑定boolean-->

<input type="checkbox" v-model='checked' id="checkbox"/>

<label for="checkbox">{{checked}}</label>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

checked: false

},

methods: {

}

})

</script>

…




选中的那些checkbox会自动被同步到checkedNames数据属性,这个属性是字符串数组,同时会在span中显示所有被选中的checkbox的值,代码如下: 



//第3章/表单输入绑定之基本用法.html

…

<div id='app'>

<!--复选框绑定数组-->

<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">

<label for="jack">Jack</label>

<input type="checkbox" id="john" value="John" v-model="checkedNames">

<label for="john">John</label>

<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">

<label for="mike">Mike</label><br>

<span>Checked names: {{ checkedNames }}</span>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

checkedNames: []

},

methods: {}

})

</script>

…




4. 单选按钮
选中某个单选按钮后,会自动被同步到picked数据属性,在span元素中同步显示,代码如下: 



//第3章/表单输入绑定之基本用法.html

…

<div id='app'>

<!--单选按钮-->



<input type="radio" id="one" value="One" v-model="picked">

<label for="one">One</label>

<input type="radio" id="two" value="Two" v-model="picked">

<label for="two">Two</label>

<br>

<span>Picked: {{ picked }}</span>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

picked: ''

},






methods: {}

})

</script>

…




5. 选择框
当选中选择框的某个选项时,会自动将选项的值同步到selected数据属性中,同时会被显示到span元素中,代码如下: 



//第3章/表单输入绑定之基本用法.html

…        

<div id='app'>

<!--选择框-->

<select v-model="selected">

<option disabled value="">请选择</option>

<option>A</option>

<option>B</option>

<option>C</option>

</select>

<span>Selected: {{ selected }}</span>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

selected: ''

},

methods: {

}

})

</script>

…




如果 vmodel 表达式的初始值未能匹配任何选项,则<select>元素将被渲染为“未选中”状态。在iOS中,这会使用户无法选择第1个选项。因为在这样的情况下,iOS不会触发change事件,因此,更推荐像上面那样提供一个值为空的禁用选项。
vmodel同时还支持多个选项的选择框,这时候可以绑定一个数组类型的数据属性。当选中多个选项时,会被自动同步到selectedArray数据属性中,同时对应的span元素会显示被选中选项的值,代码如下: 



//第3章/表单输入绑定之基本用法.html

…

<div id='app'>

<!--选择框-->








<select v-model="selectedArray" multiple style="width: 50px;">

<option>A</option>

<option>B</option>

<option>C</option>

</select>

<br>

<span>selectedArray: {{ selectedArray }}</span>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

selectedArray: []

},

methods: {}

})

</script>

…




3.3.2值绑定
对于单选按钮、复选框及选择框的选项,vmodel绑定的值通常是静态字符串(对于复选框也可以是布尔值),代码如下: 



<!-- 当选中时,`picked` 为字符串 "a" -->

<input type="radio" v-model="picked" value="a">



<!-- `toggle` 为 true 或 false -->

<input type="checkbox" v-model="toggle">



<!-- 当选中第1个选项时,`selected` 为字符串 "abc" -->

<select v-model="selected">

<option value="abc">ABC</option>

</select>




但是有时开发人员可能想把值绑定到Vue.js实例的一个动态property上,这时可以用vbind实现,并且这个property的值可以不是字符串。
1. 复选框
在checkbox元素中,添加truevalue和falsevalue属性,分别表示选中和没选中绑定的值。当选中的时候,vmodel绑定的toggle数据属性的值是yes,否则是no,代码如下: 



<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />



//当选中时






vm.toggle === 'yes'

//当没有选中时

vm.toggle === 'no'




这里的 truevalue和falsevalue属性并不会影响输入控件的 value属性,因为浏览器在提交表单时并不会包含未被选中的复选框。如果要确保表单中这两个值中的一个能够被提交(yes或no),则应换用单选按钮。

2. 单选框
当选中radio时,pick数据属性的值就是vbind绑定的值a,代码如下: 



<input type="radio" v-model="pick" v-bind:value="a">



//当选中时

vm.pick === vm.a




3. 选择框
当选中选项时,通过vmodel绑定的selected数据属性的值会被自动同步为选项中用vbind绑定的对象,代码如下: 



<select v-model="selected">

<!-- 内联对象字面量 -->

<option v-bind:value="{ number: 123 }">123</option>

</select>

//当选中时

typeof vm.selected //=> 'object'

vm.selected.number //=> 123




3.3.3修饰符
在使用vmodel绑定Vue.js实例的数据属性时,还可以添加相关的修饰符,在将数据同步到Vue.js实例的数据属性的时候,对数据进行必要预处理。例如转换成数字类型、去掉空格等。
1. lazy修饰符
在默认情况下,vmodel在每次input事件触发后会将输入框的值与数据进行同步(除了上述输入法组合文字时)。开发人员可以添加lazy修饰符,从而转换为在change事件之后进行同步,代码如下: 



<!-- 在"change"时而非"input"时更新 -->

<input v-model.lazy="msg">




2. number修饰符
如果想自动将用户的输入值转换为数值类型,则可以给 vmodel 添加 number 修饰符,代码如下: 



<input v-model.number="age" type="number">




这种方法通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat()函数解析,则会返回原始的值。

3. trim修饰符
如果要自动过滤用户输入的首尾空白字符,则可以给 vmodel 添加 trim 修饰符,代码如下: 



<input v-model.trim="msg">




3.4事件处理
在开发前端页面的时候,经常需要给html元素绑定事件代码,当对应的事件被触发时,能自动执行指定的业务代码。本小节将介绍Vue.js中对事件的处理。
3.4.1监听事件
在Vue.js中,可以使用von指令监听DOM事件,并在触发时运行JavaScript代码。使用von给button元素绑定一段JavaScript代码,每单击一次按钮,就给counter数据属性加1,代码如下: 



//第3章/事件监听.html

…

<div id='app'>

<button v-on:click="counter += 1">Add 1</button>

<p>按钮被单击了 {{ counter }} 次.</p>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

counter: 0

}

})

</script>

…




3.4.2事件处理方法
在实际项目中,许多事件处理逻辑很复杂,所以直接把JavaScript代码写在von指令中是不可行的,因此von应可以接收一个需要调用的方法名称。使用von给button元素绑定一个定义在Vue.js实例中的方法,当单击按钮的时候,自动执行Vue.js实例中绑定的方法,代码如下: 



//第3章/事件处理方法.html

…

<div id='app'>

<!-- 
`greet` 是在下面定义的方法名 -->

<button v-on:click="greet">Greet</button>



</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

data: {

name: 'Vue.js'

},

methods: {

greet: function (event) {

//`this` 在方法里指向当前Vue.js实例

alert('Hello ' + this.name + '!')

//`event` 是原生 DOM 事件

if (event) {

alert(event.target.tagName)

}

}

}

})



//也可以用 JavaScript 直接调用方法

vm.greet() //=> 'Hello Vue.js!'

</script>

…




3.4.3内联处理器中的方法
除了可以使用von给元素绑定一种方法外,也可以在内联JavaScript中调用方法。在Say hi和Say what两个按钮的内联JavaScript中调用say方法,代码如下: 



//第3章/内联处理器中调用方法.html

…

<div id='app'>

<button v-on:click="say('hi')">Say hi</button>

<button v-on:click="say('what')">Say what</button>

</div>

<script type='text/JavaScript'>






const vm = new Vue({

el: '#app',

methods: {

say: function(msg){

alert(msg)

}

}

})

</script>

…




有时也需要在内联语句处理器中访问原始的DOM事件,可以用特殊变量$event把它传入方法。使用$event把事件对象传入方法中,代码如下: 



//第3章/内联处理器中调用方法.html

…

<div id='app'>

<button v-on:click="warn('表单不能被提交')">submit </button>

</div>

<script type='text/JavaScript'>

const vm = new Vue({

el: '#app',

methods: {

warn: function (message, event) {

//现在可以访问原生事件对象了

if (event) {

//阻止事件提交

event.preventDefault()

}

alert(message)

}

}

})

</script>

…




3.4.4事件修饰符
在事件处理程序中调用 event.preventDefault()或event.stopPropagation()方法是非常常见的需求。尽管开发人员可以在方法中轻松实现这一点,但更好的方式是方法只有纯粹的数据逻辑,而不是去处理DOM事件的细节。
为了解决这个问题,Vue.js为von提供了事件修饰符。同前文提过的vmodel修饰符一样,是由点开头的指令后缀来表示的。
(1) .stop: 阻止单击事件继续传播。
(2) .prevent: 提交事件不再重载页面。
(3) .capture: 使用事件捕获模式添加事件。也就是说,如果内联元素中有其他事件,则先触发.capture修饰的事件。
(4) .self: 只触发发生在当前元素身上的事件,也就是说,事件不会由内部触发。
(5) .once: 只触发一次。
(6) .passive: 执行默认方法。浏览器只有等内核线程执行到事件监听器对应的JavaScript代码时,才能知道内部是否会调用preventDefault()方法来阻止事件的默认行为,所以浏览器本身是没有办法对这种场景进行优化的。这种场景下,用户的手势事件无法快速产生,会导致页面无法快速执行滑动逻辑,从而让用户感觉到页面卡顿。通俗地说就是每次事件产生时,浏览器都会去查询是否应由preventDefault阻止该次事件的默认动作。加上passive就是为了明确地告诉浏览器,不用查询了,这里没用preventDefault阻止默认动作。.passive修饰符一般用于滚动监听,如@scroll和@touchmove。因为在滚动监听过程中,移动每像素都会产生一次事件,每次都使用内核线程查询prevent会使滑动卡顿,通过添加passive将内核线程查询跳过,可以大大提升滑动的流畅度。
各种事件修饰符的使用和说明如下: 



<!-- 阻止单击事件继续传播 -->

<a v-on:click.stop="doThis"></a>



<!-- 提交事件不再重载页面 -->

<form v-on:submit.prevent="onSubmit"></form>



<!-- 修饰符可以串联 -->

<a v-on:click.stop.prevent="doThat"></a>



<!-- 只有修饰符 -->

<form v-on:submit.prevent></form>



<!-- 添加事件监听器时使用事件捕获模式 -->

<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->

<div v-on:click.capture="doThis">...</div>



<!-- 只在 event.target 是当前元素时触发处理函数 -->

<!-- 即事件不是从内部元素触发的 -->

<div v-on:click.self="doThat">...</div>



<!-- 单击事件将只会触发一次 -->

<a v-on:click.once="doThis"></a>



<!-- 滚动事件的默认行为 (滚动行为) 将会立即触发 -->

<!-- 而不会等待 `onScroll` 完成  -->

<!-- 这其中包含 `event.preventDefault()` 的情况 -->

<div v-on:scroll.passive="onScroll">...</div>




使用事件修饰符时的注意事项如下。
(1) .passive和.prevent冲突,不能同时绑定在同一个监听器上。
(2) 使用修饰符时,顺序很重要,相应的代码会以同样的顺序产生。
用von: click.prevent.self会阻止所有的单击,而von: click.self.prevent只会阻止对元素自身的单击。
(3) .once修饰符还能被用到自定义的组件事件上。

3.4.5按键修饰符
在监听键盘事件时,经常需要检查详细的按键。Vue.js允许为von在监听键盘事件时添加按键修饰符,对按键进行详细检查后再执行绑定的方法。当所在的键是Enter键的时候,调用vm.submit()方法,代码如下: 



<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->

<input v-on:keyup.enter="submit">




Vue.js提供了绝大多数常用的按键的别名,它们是: 
(1) .enter。
(2) .tab。
(3) .delete(捕获“删除”和“退格”键)。
(4) .esc。
(5) .space。
(6) .up。
(7) .down。
(8) .left。
(9) .right。
除了这些Vue.js提供的按键别名外,Vue.js还提供了一个机制,允许开发人员通过全局config.keyCodes对象自定义按键修饰符别名。通过config.keyCodes对象,定义v、f1、"mediaplaypause"和up4个按键别名,代码如下: 



Vue.config.keyCodes = {

v: 86,

f1: 112,

//camelCase 不可用

mediaPlayPause: 179,

//取而代之的是 kebab-case 且用双引号括起来

"media-play-pause": 179,

up: [38, 87]

}




自定义按键别名的代码如下: 



<input type="text" v-on:keyup.media-play-pause|v|f1|up="method">




3.4.6系统修饰符
除了前面的事件、按键修饰符外,Vue.js还提供了系统修饰符,实现按特殊键配合鼠标或键盘事件才能触发事件。比较常用的键是Ctrl、Alt、Shift和Meta键,它们对应的修饰符是.ctrl、.alt、.shift和.meta。注意: 在Mac系统的键盘上,Meta键对应command键()。在Windows系统的键盘上Meta键对应Windows徽标键()。在Sun操作系统的键盘上,Meta键对应实心宝石键(◆)。它们同其他键盘和鼠标的联合使用代码如下: 



<!-- Alt + C -->

<input v-on:keyup.alt.67="clear">



<!-- Ctrl + Click -->

<div v-on:click.Ctrl="doSomething">Do something</div>




需要注意的是,修饰键与常规按键不同,在和keyup事件一起使用时,当事件触发时修饰键必须处于按下状态。换句话说,只有在按住Ctrl键的情况下释放其他按键,才能触发keyup.ctrl,而单单释放Ctrl键不会触发事件。如果想要这样的行为,应将Ctrl键换作keyCode: keyup.17。
Vue 2.5以后,新增了.exact修饰符,能精准地控制系统修饰符组合事件,代码如下: 



<!-- 即使 Alt 或 Shift键被一同按下时也会触发 -->

<button v-on:click.Ctrl="onClick">A</button>



<!-- 有且只有 Ctrl键被按下的时候才触发 -->

<button v-on:click.Ctrl.exact="onCtrlClick">A</button>



<!-- 没有任何系统修饰符被按下的时候才触发 -->

<button v-on:click.exact="onClick">A</button>




最后,Vue.js还提供了3个仅响应特定的鼠标按钮的修饰符,它们分别是.left、.right和.middle,代码如下: 



<span v-on:mousedown.left='mouseLeft'>鼠标左击</span>

<span v-on:mousedown.right='mouseRight'>鼠标右击</span>

<span v-on:mousedown.middle='mouseMiddle'>鼠标中键</span>




3.5指令
指令 (Directives) 是带有 v 前缀的特殊attribute。指令的职责是,当表达式的值改变时,将其产生的连带影响响应式地作用于DOM。熟悉使用指令,能方便开发人员快速地实现页面逻辑。
3.5.1vtext和vhtml指令
vtext和vhtml的作用是在元素之间插入动态值,不同的是,vhtml会识别动态内容中的网页信息,而vtext是将网页信息当纯文本处理。
语法如下: 



<标签 v-text/v-html="表达式"></标签>




案例代码如下: 



<div id="app">

<p v-text="htmlName"></p>

<p v-html="htmlName"></p>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

htmlName:'<font color="red">张三</font>'

}

})



</script>




3.5.2vbind指令
vbind指令的作用是动态地绑定一个或多个attribute,或将一个组件prop绑定到表达式。
在绑定class或style attribute时,支持其他类型的值,如数组或对象。
在绑定prop时,prop必须在子组件中声明。可以用修饰符指定不同的绑定类型。
当没有参数时,可以绑定到一个包含键值对的对象。注意此时class和style绑定不支持数组和对象。
语法如下: 



<标签 v-bind:属性='表达式'>...</标签>




案例代码如下: 



<!-- 绑定一个 attribute -->

<img v-bind:src="imageSrc">



<!-- 动态 attribute 名 (2.6.0+) -->






<button v-bind:[key]="value"></button>



<!-- 缩写 -->

<img :src="imageSrc">



<!-- 动态 attribute 名缩写 (2.6.0+) -->

<button :[key]="value"></button>



<!-- 内联字符串拼接 -->

<img :src="'/path/to/images/' + fileName">



<!-- class 绑定 -->

<div :class="{ red: isRed }"></div>

<div :class="[classA, classB]"></div>

<div :class="[classA, { classB: isB, classC: isC }]">



<!-- style 绑定 -->

<div :style="{ fontSize: size + 'px' }"></div>

<div :style="[styleObjectA, styleObjectB]"></div>



<!-- 绑定一个全是 attribute 的对象 -->

<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>



<!-- 通过 prop 修饰符绑定 DOM attribute -->

<div v-bind:text-content.prop="text"></div>



<!-- prop 绑定。"prop"必须在 my-component 中声明。-->

<my-component :prop="someThing"></my-component>



<!-- 通过 $props 将父组件的 props 一起传给子组件 -->

<child-component v-bind="$props"></child-component>



<!-- XLink -->

<svg><a :xlink:special="foo"></a></svg>





<div id="app">

<div v-bind:id="divId">div demo</div>

<div v-bind:disabled="isdisabled">disabled</div>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

divId:'demoDiv',

isdisabled:true






}

})



console.log(document.getElementById('demoDiv'))

</script>




说明:  
如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中。
vbind可以缩写成冒号,代码如下: 



<标签 :属性='表达式'>...</标签>




3.5.3vonce指令
vonce指令的作用是控制插值只会在UI上渲染一次,当再改变属性的值时此值不会同步到UI上。
语法如下: 



<标签 v-once ...>...</标签>




案例代码如下: 



<div id="app">

<p v-once>{{userName}}</p>

<p>{{userName}}</p>

<p v-once v-bind:id="'id'+age">age{{age}}</p>

<p v-bind:id="'id'+age">age{{age}}</p>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

userName:'zhangsan',

age:12

}

})

//修改值后,不再同步到UI

vm.$data.age=14;

vm.$data.userName='lisi';

</script>




3.5.4vmodel指令
vtext和vhtml只能实现单向绑定: 对象属性能同步到UI,但是在UI上改变值后,不会同步到对象属性。vmodel指令可以实现双向绑定,经常用在表单元素上,也可以用在Vue.js的自定义Component上。
语法如下: 



<表单元素|VueComponent v-mode="表达式" ...>...</表单元素>




案例代码如下: 



<div id="app">

<input v-model="message" type="text" name="msg" placeholder="请输入消息" /><br/>

<textarea v-model="message"></textarea><br/>

<input v-model="edu" type="text" placeholder="输入学历:1,2,3">

<select v-model="edu">

<option value="1">大专</option>

<option value="2">本科</option>

<option value="3">博士</option>

</select><br/>

<input v-model="sex" placeholder="输入性别:0,1" />

<input v-model="sex" type="radio" value="0" name="sex"/>男

<input v-model="sex" type="radio" value="1" name="sex"/>女<br/>

<input v-model="likes" placeholder="输入值" />

<input v-model="likes" type="checkbox" value="0" name="likes0"/>阅读<br/>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

message:'demo',

edu:2,

sex:1,

likes:false

}

})

</script>




3.5.5vif、velseif和velse指令
vif、velseif和velse指令的作用是条件性地渲染一部分内容。
语法如下: 



<div v-if="type === 'A'">

A






</div>

<div v-else-if="type === 'B'">

B

</div>

<div v-else-if="type === 'C'">

C

</div>

<div v-else>

Not A/B/C

</div>




案例代码如下: 



<div id="app">

<div v-if="type > 0.8">

 A

</div>

<div v-else-if="type > 0.6">

 B

</div>

<div v-else-if="type > 0.4">

 C

</div>

<div v-else>

 Not A/B/C

</div>

<button v-on:click="changeType">changeType</button>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

type:Math.random()

},

methods:{

changeType:function(){

this.type=Math.random;

}

}

})

</script>




说明:  
在分支语句和vfor语句中,可能会存在相同元素需要渲染。Vue.js会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使Vue.js运行时变得非常快之外,还有其他一些好处。例如,应用允许用户在不同的登录方式之间切换,代码如下: 



<template v-if="loginType === 'username'">

<label>Username</label>

<input placeholder="Enter your username">

</template>

<template v-else>

<label>E-mail</label>

<input placeholder="Enter your email address">

</template>




在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input>不会被替换掉——仅仅替换了它的placeholder。这样不总是符合实际需求,所以Vue.js为开发人员提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的key attribute,代码如下: 



<template v-if="loginType === 'username'">

<label>Username</label>

<input placeholder="Enter your username" key="username-input">

</template>

<template v-else>

<label>E-mail</label>

<input placeholder="Enter your email address" key="email-input">

</template>




每次切换时,输入框都将被重新渲染。
3.5.6vshow指令
vshow指令的作用是根据条件展示元素。带有vshow的元素始终会被渲染并保留在DOM中。vshow只是简单地切换元素的CSS property display。
语法如下: 



<标签 v-show="表达式">... </标签>




案例代码如下: 



<div id="app">

<div v-show="ok">ok</div>

<button v-on:click="toChange">Change</button>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

ok:true






},

methods:{

toChange:function(){

this.ok = !this.ok;

}

}

})

</script>





注意: vshow 不支持<template>元素,也不支持velse。
vif同vshow的对比: 
vif是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
vif也是惰性的: 如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时才会开始渲染条件块。vshow就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换。
一般来讲,vif有更高的切换开销,而vshow有更高的初始渲染开销,因此,如果需要非常频繁地切换,则使用vshow较好; 如果在运行时条件很少改变,则使用vif较好。
3.5.7vfor指令
vfor指令的作用是遍历数组元素,并且渲染到ui。
语法如下: 



<标签 v-for="item in|of items" :key="item">...</标签>

or

<标签 v-for="(item, index) in|of items" :key="index">...</标签>





1) 遍历数组
案例代码如下: 



<div id="app">

<div v-for="item in items" :key="item.msg">

<span v-text="item.msg"></span>

</div>

<div v-for="(item, index) in items" :key="index">

<p v-text="item.msg + '->' + index"></p>

</div>



<div v-for="item of items" :key="item.msg">

<span v-text="item.msg"></span>

</div>

<div v-for="(item, index) of items" :key="index">






<p v-text="item.msg + '->' + index"></p>

</div>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

items:[

{msg:'m1'},

{msg:'m2'},

{msg:'m3'},

{msg:'m4'},

]

}

})

</script>




2) 遍历对象
案例代码如下: 



<div id="app">

<div v-for="value in user" :key='value'>

{{value}}

</div>



<div v-for="(value, name) in user" :key='name'>

{{name}}->{{value}}

</div>



<div v-for="(value, name, index) in user" :key='name+index'>

{{index}}:{{name}}->{{value}}

</div>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

user:{

name:'zhangsan',

age:12,

sex:'男',

}

}

})

</script>




3.5.8von指令
von指令的作用是绑定事件监听器。事件类型由参数指定。表达式可以是一种方法的名字或一个内联语句,如果没有修饰符,则可以省略。
当用在普通元素上时,只能监听原生DOM事件。当用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
在监听原生DOM事件时,方法以事件为唯一的参数。如果使用内联语句,则语句可以访问一个$event property: von: click="handle('ok', $event)"
语法如下: 



<标签 v-on:事件名称[.修饰符]="表达式"> ... </标签>




von指令可以缩写为 @。
von支持的修饰符如下。
.stop: 调用event.stopPropagation()。
.prevent: 调用event.preventDefault()。
.capture: 添加事件侦听器时使用capture模式。
.self: 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode |keyAlias}: 只当事件是从特定键触发时才触发回调。
.native: 监听组件根元素的原生事件。
.once: 只触发一次回调。
.left: (2.2.0)只当单击鼠标左键时触发。
.right: (2.2.0)只当右击时触发。
.middle: (2.2.0)只当单击鼠标中键时触发。
.passive: (2.3.0)以{passive: true}模式添加侦听器。

案例代码如下: 



<!-- 方法处理器 -->

<button v-on:click="doThis"></button>



<!-- 动态事件 (2.6.0+) -->

<button v-on:[event]="doThis"></button>



<!-- 内联语句 -->

<button v-on:click="doThat('hello', $event)"></button>



<!-- 缩写 -->

<button @click="doThis"></button>



<!-- 动态事件缩写 (2.6.0+) -->






<button @[event]="doThis"></button>



<!-- 停止冒泡 -->

<button @click.stop="doThis"></button>



<!-- 阻止默认行为 -->

<button @click.prevent="doThis"></button>



<!-- 阻止默认行为,没有表达式 -->

<form @submit.prevent></form>



<!--  串联修饰符 -->

<button @click.stop.prevent="doThis"></button>



<!-- 键修饰符,键别名 -->

<input @keyup.enter="onEnter">



<!-- 键修饰符,键代码 -->

<input @keyup.13="onEnter">



<!-- 单击回调只会触发一次 -->

<button v-on:click.once="doThis"></button>



<!-- 对象语法 (2.4.0+) -->

<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>




3.6Vue.js响应原理
Vue.js的一个重要特色是实现了View和Model的自动同步响应,接下来介绍一下Vue.js的响应原理和特殊数据的检测响应。
3.6.1响应式原理
当项目代码把一个普通的JavaScript对象传入Vue.js实例作为data选项时,Vue.js将遍历此对象所有的property,并使用Object.defineProperty把这些property全部转换为getter/setter。Object.defineProperty是ES5中一个无法shim(低版本不兼容)的特性,这也就是Vue.js不支持IE 8及更低版本浏览器的原因。
这些getter/setter对用户来讲是不可见的,但是在内部它们让Vue.js能够追踪依赖,在property被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对getter/setter的格式化并不同。
每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据property记录为依赖。之后当依赖项的setter被触发时,会通知watcher,从而使与它关联的组件重新被渲染,如图3.3所示。



图3.3响应式原理图



由于JavaScript的限制,Vue.js不能检测数组和对象的变化。尽管如此,状态管理工具Vuet提供了一些办法来回避这些限制并保证它们的响应性。
3.6.2对象的检测响应
Vue.js无法检测对象property的添加或移除。由于Vue.js会在初始化实例时对property执行getter/setter转换,所以property必须在data对象上存在才能让Vue.js将它转换为响应式的。
同时Vue.js不允许动态添加根级响应式property,所以开发人员必须在初始化实例前声明所有根级响应式property,哪怕只是一个空值。如果未在data选项中声明message,则Vue.js将警告渲染函数正在试图访问不存在的property,代码如下: 



var vm = new Vue({

data: {

//将 message声明为一个空值字符串

message: ''

},

template: '<div>{{ message }}</div>'

})

//之后设置 `message`

vm.message = 'Hello!'




这样的限制在背后是有其技术原因的,它消除了在依赖项跟踪系统中的一类边界情况,也使Vue.js实例能更好地配合类型检查系统工作,但与此同时在代码可维护性方面也有一点重要的考虑: data对象就像组件状态的结构(schema)。提前声明所有的响应式property,可以让组件代码在未来修改或给其他开发人员阅读时更易于理解。
对于已经创建的实例,Vue.js不允许动态添加根级别的响应式property,但是,可以使用Vue.set(object,propertyName,value)方法向嵌套对象添加响应式property,代码如下: 



Vue.set(vm.someObject, '新属性名称', 属性值)




还可以使用 vm.$set 实例方法,这也是全局 Vue.set()方法的别名,代码如下: 



this.$set(this.someObject,'b',2)




如果需要添加多个属性,则可以使用Object.assign(),将新对象的属性(可以是多个属性)与原对象的属性一起混合进一个新对象,代码如下: 



//第3章/检测对象.html

…

<div id="app">

<p>{{a}}</p>

<p>{{user.userName}}</p>

<p>{{user.age}}</p>

<p>{{user.sex}}</p>

<p>{{user.code}}</p>

<p>{{user.name}}</p>

</div>

<script>

const _address={code:'123'}

const vm = new Vue({

el:"#app",

data:{

a:1,

user:{

userName:'zhangsan',

}

}

})

vm.user.age = 12; 

//能输出age的值,但是不能响应到View

console.log(vm.user.age+","+vm.user.sex)

Vue.set(vm.user, 'sex', '男')

//输出用户对象,并且能响应到View

console.log(vm.user)

//全部合并成vm.user对象,响应到View

vm.user = Object.assign({},vm.user, {code:'001',name:'张三'})

</script>

…




3.6.3数组的检测响应
Vue.js不能检测以下数组的变动: 
(1) 当利用索引直接设置一个数组项时,Vue.js检测不到数组的变动。
例如: vm.items[indexOfItem]=newValue。
(2) 当修改数组的长度时,Vue.js检测不到数组的变动。
例如: vm.items.length=newLength。
Vue.js提供了两种方式都可以实现和vm.items[indexOfItem]=newValue相同的效果,同时也将在响应式系统内触发状态更新。一种方式是调用Vue.js的set()方法,替代数组指定下标的值; 另一种是调用数组的splice()方法,替换数组指定下标的值。可以修改数组指定索引对应元素的值,Vue.js检测不到变动的问题,代码如下: 



//Vue.set

Vue.set(vm.items, indexOfItem, newValue)

//Array.prototype.splice

//splice(index,howmany,item1…itemn)  删除从index开始的howmany个元素

//用item1..n替代这些删除的元素,并且返回删除的元素

vm.items.splice(indexOfItem, 1, newValue)




也可以使用vm.$set()实例方法,该方法是全局方法Vue.set()的一个别名,代码如下: 



vm.$set(vm.items, indexOfItem, newValue)




为了修改数组长度,Vue.js检测不到变动的问题可以使用 splice,代码如下: 



vm.items.splice(newLength)




综合示例代码如下: 



//第3章/检测数组.html

<div id="app">

<p>{{ages}}</p>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

ages:[1,2,3]

}

})

vm.ages[1] = 20;

//输出修改,但不响应到View

console.log(vm.ages)

//响应到View

Vue.set(vm.ages, 1, 21)

vm.$set(vm.ages, 1, 22)

vm.$set(vm.ages, vm.ages.length,4)

console.log(vm.ages)






//修改数组长度,响应到View

vm.ages.splice(vm.ages.length-1)

</script>




除了前面介绍的set()方法和splice()方法外,Vue.js还提供了一些其他方法,实现变更数组和替换数组,同时还能检测到数组的变化。
1. 变更数组方法
变更方法会改变以前的数组(原数组)。Vue.js将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。被包裹的变更数组的方法如下。
1) push()
在数组中添加一个新元素,并返回数组的新长度值,代码如下: 



arrayObj.push([item1 [item2 [. . .  [itemN ]]]])




2) pop()
移除数组中的最后一个元素并返回该元素,代码如下: 



arrayObj.pop()




3) shift()
移除数组中的第1个元素并返回该元素,代码如下: 



arrayObj.shift()




4) unshift()
将指定的元素插入数组的开始位置并返回新数组的长度,代码如下: 



arrayObj.unshift([item1[, item2 [, .  . . [, itemN]]]])




5) splice()
从一个数组中移除一个或多个元素,如果必要,则在所移除元素的位置上插入新元素,并且返回所移除的元素,代码如下: 



arrayObj.splice(start, deleteCount,[item1[,  item2[, . . . [,itemN]]]])




6) sort()
返回一个对元素完成排序的数组,代码如下: 



arrayobj.sort(sortfunction)




如果为 sortfunction 参数提供了一个函数,则表示在执行sort时,基于sortfunction对元素进行大小比较。该函数必须返回下列值之一: 
(1) 负值,如果所传递的第1个参数比第2个参数小。
(2) 零,如果两个参数相等。
(3) 正值,如果第1个参数比第2个参数大。

7) reverse()
返回一个数组元素被反转的数组,代码如下: 



arrayObj.reverse()




2. 替换数组方法
替换返回,不会改变以前的数组,但是会返回一个新的数组。
1) filter()
根据filter条件返回新数组,代码如下: 



array.filter(function(currentValue, indedx, arr), thisValue)




currentValue: 必须,当前元素的值;  index: 可选,当前元素的索引值; arr: 可选,当前元素属于的数组对象; thisValue: 可选,对象作为该执行回调时使用,传递给函数,用作"this"的值。如果省略了thisValue,则"this"的值为"undefined"。以下代码演示了filter方法的使用: 



var arr = [3,9,4,3,6,0,9];

//返回大于5的值

function max5(arr){

return arr.filter(x) =(> x > 5);

}

//移除数组中与item相同的值并返回

function remove(arr, item) {

return arr.filter(val => val != item);

}

//移除重复值

var r = arr.filter(function (element, index, self) {

return self.indexOf(element) == index;

});

//查找所有同item值相同的元素的位置

function findAllOccurrences(arr, target) {

var res=[];

arr.filter(function(ele,index){

return (ele===target)&&res.push(index);

})

return res;

}




2) concat()
返回一个新数组,这个新数组是由两个或更多个数组组合而成的,代码如下: 



array1.concat([item1[, item2[, . . . [,  itemN]]]])




3) slice()
截取一段子数组并返回,代码如下: 



arrayObject.slice(start,end)//包含开始,不包含结尾




3.6.4异步更新问题
Vue.js在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue.js将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,则只会被推入队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的,然后,在下一个事件的循环tick中,Vue.js将刷新队列并执行实际(已去重的)工作。Vue.js在内部对异步队列尝试使用原生的Promise.then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn,0)代替。
例如,如果设置vm.someData='new value',则该组件不会立即被重新渲染。当刷新队列时,组件会在下一个事件循环tick中更新。多数情况下开发人员不需要关心这个过程,但是如果开发人员想基于更新后的DOM状态来做点什么,这就可能会有些棘手。虽然Vue.js通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触DOM,但是有时开发人员必须这么做。为了在数据变化之后等待Vue.js完成更新DOM操作,可以在数据变化之后立即使用Vue.nextTick(callback)。这样回调函数将在DOM更新完成后被调用,代码如下: 



<div id="app">

<div>{{message}}</div>

</div>

<script>

const vm = new Vue({

el:"#app",

data:{

message:"123"

}

})

vm.message = 'new message' //更改数据

let bool = vm.$el.textContent === 'new message' //false

console.log(bool)

Vue.nextTick(function () {






 let bool = vm.$el.textContent === 'new message' //true

 console.log(bool)

})

</script>




在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue.js,并且回调函数中的 this 将自动被绑定到当前的Vue.js实例上,代码如下: 



Vue.component('example', {

template: '<span>{{ message }}</span>',

data: function ) {

return {

message: '未更新'

}

},

methods: {

updateMessage: function () {

this.message = '已更新'

console.log(this.$el.textContent) //=> '未更新'

this.$nextTick(function () {

console.log(this.$el.textContent) //=> '已更新'

})

}

}

})




因为$nextTick()会返回一个Promise对象,所以开发人员可以使用新的ES2017 async/await 语法完成相同的事情,代码如下: 



methods: {

updateMessage: async function () {

this.message = '已更新'

console.log(this.$el.textContent) //=> '未更新'

await this.$nextTick()

console.log(this.$el.textContent) //=> '已更新'

}

}