vue总结 01基础特性
最近有时间来总结一下vue的知识:
一、vue.js 被定义成一个开发web界面的前端库,是一个非常轻量的工具。vue.js本身具有响应式和组件化的特点。
我们不需要在维护视图和数据的统一上花费大量的时间,只需要关注于 data 的变化,代码变得更加容易维护。
但是要选择框架还是要根据实际情况。
理想的情况下,vue更适合做前后端分离的项目。
二、vue的基础特性(无论前端框架怎么变化,它需要处理的事情依旧是模板渲染、事件绑定、处理用户交互(输入信息或者鼠标操作))
vue.js是通过一个实例new Vue({.....})标记当前页面的HTML结构、数据的展示及相关事件的绑定。 vue.js的构造函数的选项对象及用法,以及如何通过vue.js来实现上述前端功能。
1.实例及选项
1.1模板
1.2数据
1.3方法
1.4生命周期
2.数据绑定
2.1数据绑定语法
2.1.1文本插值
2.1.2HTML属性
2.1.3绑定表达式
2.1.4过滤器
2.1.5指令
2.2计算属性
2.3表单控件
2.4class与style绑定
3.模板渲染
3.1前后端渲染对比
3.2条件渲染
3.3列表渲染
3.4template标签用法
4.事件绑定与监听
4.1方法及内联语句处理器
4.2修饰符
4.3与传统事件绑定的区别
5.vue.extend()
1.实例及选项
vue.js的使用都是通过构造函数Vue({option})创建一个vue的实例:var vm = new Vue({})。一个Vue实例相当于一个MVVM模式中的ViewModel.
在实例化的时候,我们可以传入一个选项对象,包含数据、模板、挂载元素、方法、生命周期钩子等选项。
1.1模板
选项中主要影响模板或DOM的选项有el和template。
el:类型为字符串,DOM元素或函数。其作用是为实例提供挂载元素。一般来说,我们会选用css选择符,或者原生的DOM元素。
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
在实例挂载之后,元素可以用 vm.$el
访问。
如果这个选项在实例化时有作用,实例将立即进入编译过程,否则,需要显式调用 vm.$mount()
手动开启编译。
template:类型为字符串。默认会将template值替换挂载元素(即el值对应的元素),并合并挂载元素和模板根节点的属性。
一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。
render:字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。
该渲染函数接收一个 createElement
方法作为第一个参数用来创建 VNode
。
如果组件是一个函数组件,渲染函数还会接收一个额外的 context
参数,为没有实例的函数组件提供上下文信息。
rendererror:只在开发者环境下工作。
当 render
函数遭遇错误时,提供另外一种渲染输出。其错误将会作为第二个参数传递到 renderError
。这个功能配合 hot-reload 非常实用。
new Vue({
render (h) {
throw new Error('oops')
},
renderError (h, err) {
return h('pre', { style: { color: 'red' }}, err.stack)
}
}).$mount('#app')
1.2数据
Vue实例的数据对象。Vue将会递归将data的属性转换为getter/setter,从而让打他的属性能够响应数据变化。对象必须是纯粹的对象(含有零个或多个的key/value对):浏览器API创建的原生对象,原型上的属性会被忽略。大概来说,data应该只能是数据-不推荐观察拥有状态行为的对象。
一旦观察过,不需要再次在数据对象上添加响应式属性。因此推荐在创建实例之前,就声明所有的根级响应式属性。
实例创建之后,可以通过vm.$data访问原始数据对象。Vue实例也代理了data对象上所有的属性,因此访问vm.a等价于访问vm.$data.a。
以 _ 或$ 开头的属性不会被Vue实例代理,因为他们可能和Vue内置的属性、API方法冲突。你可以使用例如vm.$data._property的方式访问这些属性。
当一个组件被定义,data必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果data仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供data函数,每次创建一个新实例后,我们能够调用data函数,从而返回初始数据的一个全新副本数据对象。
如果需要,可以通过将vm.$data传入JSON.parse(JSON.stringify(...))得到深拷贝的原始数据对象。
1 var data = { a: 1 } 2 3 // 直接创建一个实例 4 var vm = new Vue({ 5 data: data 6 }) 7 vm.a // => 1 8 vm.$data === data // => true 9 10 // Vue.extend() 中 data 必须是函数 11 var Component = Vue.extend({ 12 data: function () { 13 return { a: 1 } 14 } 15 })
注意:如果你为data属性使用了箭头函数,则this不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。
data: vm => ({ a: vm.myProp })
Vue.js实例中可以通过data属性定义数据,这些数据可以在实例对应的模板中进行绑定并使用。需要注意的是,如果传入data的是一个对象,vue实例会代理起data对象的所有属性,而不会对传入的对象进行深拷贝。另外,我们也可以引用Vue实例vm中的$data来获取声明的数据。
1 var data = { a: 1 } 2 var vm = new Vue({ 3 data: data 4 }) 5 vm.$data === data // -> true 6 vm.a === data.a // -> true 7 // 设置属性也会影响到原始数据 8 vm.a = 2 9 data.a // -> 2 10 // 反之亦然 11 data.a = 3 vm.a // -> 3
需要注意的是,只有初始化时传入的对象才是响应式的。所以,我们应尽量在初始化的时候,把所有的变量都设定好,如果没有值,也可以用undefined或null占位。另外,组件类型的实例可以通过props获取数据,通data一样,也需要在初始化时预设好。
1.3方法
methods将被混入到vue实例中。可以直接通过vm实例访问这些方法,或者在指令表达式中使用。方法中的this自动绑定为Vue实例。
注意:不应该使用箭头函数来定义method函数(例如plus:()=> this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以this将不会按照期望指向Vue实例,this.a将是undefined。
1 var vm = new Vue({ 2 data: { a: 1 }, 3 methods: { 4 plus: function () { 5 this.a++ 6 } 7 } 8 }) 9 vm.plus() 10 vm.a // 2
我们可以通过选项属性methods对象来定义方法,并使用v-on指令来监听DOM事件。
1 <button v-on:click="alert"/>alert<button> new Vue({ 2 el : '#app', 3 data : { a : 1}, 4 methods : { 5 alert : function() { alert(this.a); } 6 } 7 });
1.4生命周期
下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
created:在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el
属性目前不可见。
beforeMount:在挂载开始之前被调用:相关的 render
函数首次被调用。该钩子在服务器端渲染期间不被调用。
mounted:el
被新创建的 vm.$el
替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted
被调用时 vm.$el
也在文档内。该钩子在服务器端渲染期间不被调用。
注意 mounted
不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted
:
1 mounted: function () { 2 this.$nextTick(function () { 3 // Code that will run only after the 4 // entire view has been rendered 5 }) 6 }
beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。该钩子在服务器端渲染期间不被调用。
注意 updated
不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用 vm.$nextTick 替换掉 updated
:
1 updated: function () { 2 this.$nextTick(function () { 3 // Code that will run only after the 4 // entire view has been re-rendered 5 }) 6 }
activated:keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。
deactivated:keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用。
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
errorCaptured:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false
以阻止该错误继续向上传播。
你可以在此钩子中修改组件的状态。因此在模板或渲染函数中设置其它内容的短路条件非常重要,它可以防止当一个错误被捕获时该组件进入一个无限的渲染循环。
错误传播规则
默认情况下,如果全局的 config.errorHandler 被定义,所有的错误仍会发送它,因此这些错误仍然会向单一的分析服务的地方进行汇报。
如果一个组件的继承或父级从属链路中存在多个 errorCaptured 钩子,则它们将会被相同的错误逐个唤起。
如果此 errorCaptured 钩子自身抛出了一个错误,则这个新错误和原本被捕获的错误都会发送给全局的 config.errorHandler。
一个 errorCaptured 钩子能够返回 false 以阻止错误继续向上传播。本质上是说“这个错误已经被搞定了且应该被忽略”。它会阻止其它任何会被这个错误唤起的 errorCaptured 钩子和全局的 config.errorHandler。
1 var vm = new Vue({ 2 el : '#app', 3 init: function() { 4 console.log('init'); }, 5 created: function() { 6 console.log('created'); 7 }, 8 beforeCompile: function() { console.log('beforeCompile'); }, 9 compiled: function() { 10 console.log('compiled'); 11 }, 12 attached: function() { 13 console.log('attached'); }, 14 dettached: function() { 15 console.log('dettached'); }, 16 beforeDestroy: function() { console.log('beforeDestroy'); }, 17 destroyed: function() { 18 console.log('destroyed'); }, 19 ready: function() { 20 console.log('ready'); 21 // 组件完成后调用$destory()函数,进行销毁 22 this.$destroy(); } 23 });
输出结果为:
2.数据绑定
2.1数据绑定语法
1 var vm = new Vue({ el : '#app', 2 data: { 3 id:1, 4 index : 0, 5 name : 'Vue', 6 avatar : 'http://......' 7 count:[1,2,3,4,5] 8 names : ['Vue1.0', 'Vue2.0'], items:[ 9 { name : 'Vue1.0', version : '1.0' }, 10 { name : 'Vue1.1', version : '1.0' } 11 ] 12 } 13 });
2.1.1文本插值
数据绑定最基础的形式就是文本插值,使用的是双大括号{{}}。
vue.js实例vm中name属性的值将会替换Mustache标签中的name,并且修改数据对象中的name属性,DOM也会随之更新。
2.1.2HTML属性
Museache标签也同样适用于HTML属性中。
<div v-bind:id="'id-'+id"></div> 简写为<div :id="'id-' + id"></div>
2.1.3绑定表达式
放在Mustache标签内的文本内容称为绑定表达式。
除了直接输出属性值之外,一段绑定表达式可以由一个简单的JavaScript表达式和可选的一个或多个过滤器构成。
每个绑定中只能包含单个表达式,并不支持JavaScript语句。
并且绑定表达式里不支持正则表达式,如果需要进行复杂的转换,可以使用过滤器或者计算属性来进行处理。
2.1.4过滤器
vue.js允许在表达式后添加可选的过滤器,以管道符“|”指示。
vue.js内置了10个过滤器:
>1.capitalize:字符串首字符转化成大写
>2.uppercase:字符串转化成大写
>3.lowercase:字符串转化成小写
>4.currency参数为{string}[货币符号],{Number}[小数位],将数字转化成货币符号,并且会自动添加数字分节号。
{{ amount | cyrrency '¥' 2 }} //->若amount=1000,则输出¥1,000.00
>5.pluralize参数为{String}single,[double, triple],字符串复数化。如果接受的是一个参数,那复数形式就是在字符串末尾直接加一个“s”。
如果接受多个参数,则会被当成数组处理,字符串会添加对应数组下标的值。
如果字符串的个数多于参数个数,多出部分会都添加最后一个参数的值。
<p v-for="c in count">{{ c | pluralize 'item' }} {{ c | pluralize 'st' 'nd' 'rd' 'th' }}</p>
>6.json参数为{Number}[indent]空格缩进数,与JSON.stringify()作用相同,将json对象数据输出成符合json格式的字符串。
>7.debounce传入值必须是函数,参数可选,为{Number}[wait],即延时时长。
作用是当调用函数n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。
<input v-on:keyup="onKeyup | debounce 500"> //input元素上监听了keyup事件,并且延迟500ms触发。
>8.limitBy传入值必须是数组,参数为{Number}limit,{Number}[offset],limit为显示个数,offset为开始显示数组下标。
<div v-for="item in items | limitBy 10"></div> //items为数组,且只显示数组中的前十个元素。
>9.filterBy传入值必须是数组,参数为{String | Function}targetStringOrFunction,
即需要匹配的字符串或函数(通过函数返回值true或false来判断匹配结果);
“in”(可选分隔符);{String}[...searchKeys],为检索的属性区域。
<p v-for="name in names | filterBy '1.0'">{{name}}</p> //检索items数组中值包含1.0的元素
<p v-for="item in items | filterBy '1.0' in 'name'">{{ item | json }}</p> //检索items数组中元素属性值为1.0的元素输出。检索区域也可以为数组
即in[name, version],在多个属性中进行检索。
<p v-for="item in items | filterBy customFilter">{{ item | json }}</p> //使用自定义的过滤函数,函数可以在选项methods中定义
methods: {
customFilter : function(item){
if(item.name) return true //检索所有元素中包含name属性的元素
}
}
>10.orderBy 传入值必须是数组,参数为{String | Array | Function }sortKeys,即指定排序策略。
这里可以使用单个键名,也可以传入包含多个排序键名的数组。也可以像Array.Sort()那样传入自己的排序策略函数。
第二个参数为可选参数{String}[order], 即选择升序或降序,order>=0为升序,order<0为降序。
单个键名:<p v-for="item in items | orderBy 'name' -1 ">{{item.name}}</p> //items数组中以键名name进行降序排序
多个键名:<p v-for="item in items | orderBy [name, version]">{{item.name}}</p> //使用items里的两个键名进行排序
自定义排序函数:<p v-for="item in items | orderBy curtomOrder">{{item.name}}</p>
methods : {
customOrder : function (a, b) {
return parseFloat(a.version) > parseFloat(b.version) //对比item中version的值的大小进行排序
}
}
2.1.5指令
可以理解为当表达式的值发生变化时,会有些特殊行为作用到绑定的DOM上。
指令通常会直接书写在模板的HTML元素中,而为了有别于普通的属性, Vue.js指令是带有前缀的v-的属性上。
指令值限定为绑定表达式,JavaScript表达式以及过滤器规则在这里也适用。
2.1.5.1参数
<img v-bind:src="avatar"> //指令v-bind可以在后面带一个参数,用冒号(:)隔开,src即为参数。
此时img标签中的src会与vm实例中的avatar绑定,等同于<img src="{{avatar}}"/>
2.1.5.2修饰符
修饰符是以半角句号.开始的特殊后缀,用于表示指令应该以特殊方式绑定。<button v-on:click.stop="doClick"></button>
v-on的作用是在对应的DOM元素上绑定事件监听器,doClick为函数名,而stop即为修饰符,
作用是停止冒泡,相当于调用了e.stopPropagation()。
2.2计算属性
避免在模板中加入过重的业务逻辑,保证模板的结构清晰和可维护性。
2.2.1基础例子
var vm = new Vue({
el : '#app',
data : {
firstName : 'Gavin',
lastName : 'CLV'
}
computed : {
fullName : function(){
return this.fristName + ' ' + this.lastName
}
}
});
<p>{{ firstName }}</p> //Gavin
<p>{{ lastName }}</p> //CLY
<p>{{ fullName }}</p> //Gavin CLY
此时,你对vm.firstName和vm.lastName进行修改,始终会影响vm.fullName.
2.2.2Setter
var vm = new Vue({
el : '#el',
data : {
cents : 100,
}
computed : {
price : {
set : function(newValue){
this.cents = newValue * 100;
},
get : function(){
return(this.cents/100).toFixed(2);
}
}
}
});
在处理商品价格的时候,后端往往会把价格定义成以分为单位的整数,避免在处理浮点类型数据时产生的问题。
而前端则需要把价钱再转换成元进行展示,而且如果需要对价钱进行修改的话,则又要把输入的价格再恢复到分传给后端,很是繁琐。
而在使用vue的计算属性后,我们可以将vm.cents设置为后端所存的数据,计算属性price为前端展示和更新的数据。
<p>¥{{price}}</p> //¥1.00
此时更改vm.price = 2, vm.cents会被更新为200,在传递给后端时无需再手动转化一遍数据。
2.3表单控件
Vue.js中提供v-model的指令对表单元素进行双向数据绑定,在修改表单元素值的同时,实例vm中对应的属性值也同时更新,反之亦然。
var vm = new Vue({
el : '#app',
data : {
message : '',
gender : '',
checked : '',
multiChecked : [],
selected : '',
multiSelected: []
}
});
2.3.1Text :输入框
用户输入的内容和vm.message直接绑定:
<input type="text" v-model="message"/>
<span>Your input is : {{ message }}</span>
2.3.2Radio :单选框
<label><input type="radio" value="male" v-model="gender">男</label>
<label><input type="radio" value="female" v-model="gender">女</label>
2.3.3Checkbox
checkbox分两种情况:单个勾选框和多个勾选框。
单个勾选框,v-model即为布尔值,此时input的value并不影响v-model的值。
<input tyep="checkbox" v-model="checked"/>
<span>checked : {{ checked }}</span>
多个勾选框,v-model使用相同的属性名称,且属性为数组。
<label><input type="checkbox" value="1" v-model="multiChecked">1</label>
<label><input type="checkbox" value="2" v-model="multiChecked">2</label>
<label><input type="checkbox" value="3" v-model="multiChecked">3</label>
<p>MultiChecked: {{ multiCheched.join('|') }}</p>
2.3.4Select
单选:
<select v-model="selected">
<option selected>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
多选:
<select v-model="multiSelected" multiple>
<option selected>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>MultiSelected: {{ multiCheched.join('|') }}</span>
2.3.5绑定value
表单控件的值同样可以绑定Vue实例的动态属性上,用v-bind实现。
1.Checkbox
<input type="checkbox" v-model="checked" v-bind:true-value="a" v-bind:false-value="b">
选中:vm.checked == vm.a // ->true
未选中:vm.checked == vm.b // ->false
2.Radio
<input type="radio" v-model="checked", v-bind:value="a">
选中:vm.checked == vm.a // ->true
3.Select Options
<select v-model="seleced">
<option v-bind:value="{ number : 123 }">123</option>
</select>
选中:
typeof vm.selected // ->'object'
vm.selected.number //->123
2.3.6参数特性
Vue.js为表单控件提供了一些参数,方便处理某些常规操作。
2.3.6.1、lazy
默认情况下,v-model在input事件中同步输入框值与数据,加lazy属性后从会改到在change事件中同步
<input v-model="query" lazy/>
2.3.6.2、number
会自动将用户输入转为Number类型,如果原值转换结果为NaN则返回原值。
<input v-model="age" number/>
2.3.6.3、debounce
debounce为设置的最小延时,单位为ms,即为单位时间内仅执行一次数据更新。
该参数往往应用在高耗操作上,例如在更新时发出ajax请求返回提示信息。
<input v-model="query" debounce="500"/>
不过vue.js2.0中取消了lazy和number作为参数,用修饰符来代替
2.4class与style绑定
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。
因为它们都是属性,所以我们可以用 v-bind
处理它们:只需要通过表达式计算出字符串结果即可。
不过,字符串拼接麻烦且易错。因此,在将 v-bind
用于 class
和 style
时,Vue.js 做了专门的增强。
表达式结果的类型除了字符串之外,还可以是对象或数组。
2.4.1、class绑定
我们可以传给 v-bind:class
一个对象,以动态地切换 class:
<div v-bind:class="{ active: isActive }"></div>
|
上面的语法表示 active
这个 class 存在与否将取决于数据属性 isActive
的 truthiness。
你可以在对象中传入更多属性来动态切换多个 class。此外,v-bind:class
指令也可以与普通的 class 属性共存。当有如下模板:
<div class="static"
|
和如下 data:
data: {
|
结果渲染为:
<div class="static active"></div>
|
当 isActive
或者 hasError
变化时,class 列表将相应地更新。
例如,如果 hasError
的值为 true
,class 列表将变为 "static active text-danger"
。
绑定的数据对象不必内联定义在模板里:
<div v-bind:class="classObject"></div>
|
data: {
|
渲染的结果和上面一样。我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:
<div v-bind:class="classObject"></div>
|
data: {
|
数组语法
我们可以把一个数组传给 v-bind:class
,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>
|
data: {
|
渲染为:
<div class="active text-danger"></div>
|
如果你也想根据条件切换列表中的 class,可以用三元表达式:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
|
这样写将始终添加 errorClass
,但是只有在 isActive
是 truthy[1] 时才添加 activeClass
。
不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
|
用在组件上
这个章节假设你已经对 Vue 组件有一定的了解。当然你也可以先跳过这里,稍后再回过头来看。
当在一个自定义组件上使用 class
属性时,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。
例如,如果你声明了这个组件:
Vue.component('my-component', {
|
然后在使用它的时候添加一些 class:
<my-component class="baz boo"></my-component>
|
HTML 将被渲染为:
<p class="foo bar baz boo">Hi</p>
|
对于带数据绑定 class 也同样适用:
<my-component v-bind:class="{ active: isActive }"></my-component>
|
当 isActive
为 truthy[1] 时,HTML 将被渲染成为:
<p class="foo bar active">Hi</p>
|
2.4.2、内联样式绑定
对象语法
v-bind:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
|
data: {
|
直接绑定到一个样式对象通常更好,这会让模板更清晰:
<div v-bind:style="styleObject"></div>
|
data: {
|
同样的,对象语法常常结合返回对象的计算属性使用。
数组语法
v-bind:style
的数组语法可以将多个样式对象应用到同一个元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
|
自动添加前缀
当 v-bind:style
使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform
,Vue.js 会自动侦测并添加相应的前缀。
多重值
2.3.0+
从 2.3.0 起你可以为 style
绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
|
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex
。
译者注
[1] truthy 不是 true
,详见 MDN 的解释。
3.模板渲染
当获取到后台数据后,我们会把它按照一定的规则加载到写好的模板中,输出成在浏览器中显示的HTML,这个过程就称为渲染。
3.1前后端渲染对比
3.2条件渲染
v-if
在字符串模板中,比如 Handlebars,我们得像这样写一个条件块:
<!-- Handlebars 模板 -->
|
在 Vue 中,我们使用 v-if
指令实现同样的功能:
<h1 v-if="ok">Yes</h1>
|
也可以用 v-else
添加一个“else 块”:
<h1 v-if="ok">Yes</h1>
|
在<template>元素上使用v-if条件渲染分组
因为 v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?
此时可以把一个 <template>
元素当做不可见的包裹元素,并在上面使用 v-if
。最终的渲染结果将不包含 <template>
元素。
<template v-if="ok">
|
v-else
你可以使用 v-else
指令来表示 v-if
的“else 块”:
<div v-if="Math.random() > 0.5">
|
v-else
元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别。
v-else-if
2.1.0 新增
v-else-if
,顾名思义,充当 v-if
的“else-if 块”,可以连续使用:
<div v-if="type === 'A'">
|
类似于 v-else
,v-else-if
也必须紧跟在带 v-if
或者 v-else-if
的元素之后。
用key管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:
<template v-if="loginType === 'username'">
|
那么在上面的代码中切换 loginType
将不会清除用户已经输入的内容。
因为两个模板使用了相同的元素,<input>
不会被替换掉——仅仅是替换了它的 placeholder
。
自己动手试一试,在输入框中输入一些文本,然后按下切换按钮:
这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。
只需添加一个具有唯一值的 key
属性即可:
<template v-if="loginType === 'username'">
|
现在,每次切换时,输入框都将被重新渲染。请看:
注意,<label>
元素仍然会被高效地复用,因为它们没有添加 key
属性。
v-show
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
|
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display
。
注意,v-show
不支持 <template>
元素,也不支持 v-else
。
v-if vs v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。
因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
v-if v-for一起使用
当 v-if
与 v-for
一起使用时,v-for
具有比 v-if
更高的优先级。
请查阅 列表渲染指南 以获取详细信息。
3.3列表渲染
用v-for把一个数组对应为一组元素
我们用 v-for
指令根据一组数组的选项列表进行渲染。
v-for
指令需要使用 item in items
形式的特殊语法,items
是源数据数组并且 item
是数组元素迭代的别名。
<ul id="example-1">
|
var example1 = new Vue({
|
结果:
- Foo
- Bar
在 v-for
块中,我们拥有对父作用域属性的完全访问权限。v-for
还支持一个可选的第二个参数为当前项的索引。
<ul id="example-2">
|
var example2 = new Vue({
|
结果:
- Parent - 0 - Foo
- Parent - 1 - Bar
你也可以用 of
替代 in
作为分隔符,因为它是最接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>
|
一个对象的v-for
你也可以用 v-for
通过一个对象的属性来迭代。
<ul id="v-for-object" class="demo">
|
new Vue({
|
结果:
- John
- Doe
- 30
你也可以提供第二个的参数为键名:
<div v-for="(value, key) in object">
|
第三个参数为索引:
<div v-for="(value, key, index) in object">
|
在遍历对象时,是按 Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。
key
当 Vue.js 用 v-for
正在更新已渲染过的元素列表时,它默认用“就地复用”策略。
如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,
并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index"
。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
属性。
理想的 key
值是每项都有的且唯一的 id。这个特殊的属性相当于 Vue 1.x 的 track-by
,但它的工作方式类似于一个属性,
所以你需要用 v-bind
来绑定动态值 (在这里使用简写):
<div v-for="item in items" :key="item.id">
|
建议尽可能在使用 v-for
时提供 key
,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key
并不与 v-for
特别关联,key 还具有其他用途,
我们将在后面的指南中看到其他用途。
数组更新检测
变异方法
Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你打开控制台,然后用前面例子的 items
数组调用变异方法:example1.items.push({ message: 'Baz' })
。
替换数组
变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异 (non-mutating method) 方法,
例如:filter()
, concat()
和 slice()
。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
|
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。
幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的、启发式的方法,
所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
注意事项
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
- 当你利用索引直接设置一个项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
- 当你利用索引直接设置一个项时,例如:
举个例子:
var vm = new Vue({
|
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue
相同的效果,同时也将触发状态更新:
// Vue.set
|
// Array.prototype.splice
|
你也可以使用 vm.$set
实例方法,该方法是全局方法 Vue.set
的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
|
为了解决第二类问题,你可以使用 splice
:
vm.items.splice(newLength)
|
对象更改检测注意事项
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
|
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。
但是,可以使用 Vue.set(object, key, value)
方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
|
你可以添加一个新的 age
属性到嵌套的 userProfile
对象:
Vue.set(vm.userProfile, 'age', 27)
|
你还可以使用 vm.$set
实例方法,它只是全局 Vue.set
的别名:
vm.$set(vm.userProfile, 'age', 27)
|
有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign()
或 _.extend()
。
在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:
Object.assign(vm.userProfile, {
|
你应该这样做:
vm.userProfile = Object.assign({}, vm.userProfile, {
|
显示过滤/排序结果
有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。
在这种情况下,可以创建返回过滤或排序数组的计算属性。
例如:
<li v-for="n in evenNumbers">{{ n }}</li>
|
data: {
|
在计算属性不适用的情况下 (例如,在嵌套 v-for
循环中) 你可以使用一个 method 方法:
<li v-for="n in even(numbers)">{{ n }}</li>
|
data: {
|
一段取值范围的 v-for
v-for
也可以取整数。在这种情况下,它将重复多次模板。
<div>
|
结果:
v-for
on a <template>
类似于 v-if
,你也可以利用带有 v-for
的 <template>
渲染多个元素。比如:
<ul>
|
v-for
with v-if
当它们处于同一节点,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-for
循环中。
当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
|
上面的代码只传递了未完成的 todos。
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if
置于外层元素 (或 <template>
)上。如:
<ul v-if="todos.length">
|
一个组件的 v-for
了解组件相关知识,查看 组件。完全可以先跳过它,以后再回来查看。
在自定义组件里,你可以像任何普通元素一样用 v-for
。
<my-component v-for="item in items" :key="item.id"></my-component>
|
2.2.0+ 的版本里,当在组件中使用
v-for
时,key
现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props
:
<my-component
|
不自动将 item
注入到组件里的原因是,这会使得组件与 v-for
的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。
下面是一个简单的 todo list 的完整例子:
<div id="todo-list-example">
|
注意这里的 is="todo-item"
属性。这种做法在使用 DOM 模板时是十分必要的,
因为在 <ul>
元素内只有 <li>
元素会被看作有效内容。这样做实现的效果与 <todo-item>
相同,
但是可以避开一些潜在的浏览器解析错误。查看 DOM 模板解析说明 来了解更多信息。
Vue.component('todo-item', {
|
- Do the dishes
- Take out the trash
- Mow the lawn
4.事件绑定与监听
当模板渲染完成之后,就可以进行事件的绑定与监听了。
Vue.js提供了v-on指令用来监听DOM事件,通常在模板内直接使用,而不像传统方式在js中获取DOM元素,然后绑定事件。
<button v-on:click="say">say</button>
监听事件
可以用 v-on
指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
示例:
<div id="example-1">
|
var example1 = new Vue({
|
结果:
The button above has been clicked 0 times.
事件处理方法
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on
指令中是不可行的。因此 v-on
还可以接收一个需要调用的方法名称。
示例:
<div id="example-2">
|
var example2 = new Vue({
|
结果:
内联处理器中的方法
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:
<div id="example-3">
|
new Vue({
|
结果:
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法:
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
|
// ...
|
事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。
尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on
提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop
.prevent
.capture
.self
.once
<!-- 阻止单击事件继续传播 -->
|
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
2.1.4 新增
<!-- 点击事件将只会触发一次 -->
|
不像其它只能对原生的 DOM 事件起作用的修饰符,.once
修饰符还能被用到自定义的组件事件上。如果你还没有阅读关于组件的文档,现在大可不必担心。
2.3.0 新增
Vue 还对应 addEventListener
中的 passive
选项提供了 .passive
修饰符。
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
|
这个 .passive
修饰符尤其能够提升移动端的性能。
不要把 .passive
和 .prevent
一起使用,因为 .prevent
将会被忽略,同时浏览器可能会向你展示一个警告。
请记住,.passive
会告诉浏览器你不想阻止事件的默认行为。
按键修饰符
在监听键盘事件时,我们经常需要检查常见的键值。Vue 允许为 v-on
在监听键盘事件时添加按键修饰符:
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
|
记住所有的 keyCode
比较困难,所以 Vue 为最常用的按键提供了别名:
<!-- 同上 -->
|
全部的按键别名:
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
|
自动匹配按键修饰符
2.5.0 新增
你也可直接将 KeyboardEvent.key
暴露的任意有效按键名转换为 kebab-case 来作为修饰符:
<input @keyup.page-down="onPageDown">
|
在上面的例子中,处理函数仅在 $event.key === 'PageDown'
时被调用。
有一些按键 (.esc
以及所有的方向键) 在 IE9 中有不同的 key
值, 如果你想支持 IE9,它们的内置别名应该是首选。
系统修饰键
2.1.0 新增
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
例如:
<!-- Alt + C -->
|
请注意修饰键与常规按键不同,在和 keyup
事件一起用时,事件触发时修饰键必须处于按下状态。
换句话说,只有在按住 ctrl
的情况下释放其它按键,才能触发 keyup.ctrl
。而单单释放 ctrl
也不会触发事件。
如果你想要这样的行为,请为 ctrl
换用 keyCode
:keyup.17
。
.exact
修饰符
2.5.0 新增
.exact
修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
|
鼠标按钮修饰符
2.2.0 新增
.left
.right
.middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮。
为什么在 HTML 中监听事件?
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。
但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。
实际上,使用 v-on
有几个好处:
-
扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
-
因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
-
当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
4.3与传统事件绑定的区别
5.vue.extend()