v2.6
模板语法
可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<a v-bind:[attributeName]="url"> ... </a>
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
对动态参数的值的约束
动态参数预期会求出一个字符串,异常情况下值为 null。
这个特殊的 null 值可以被显性地用于移除绑定。
任何其它非字符串类型的值都将会触发一个警告。
计算属性缓存vs方法
计算属性基于它们的响应式依赖进行缓存,
这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:
1 2 3 4 5
| computed: { now: function () { return Date.now() } }
|
计算属性的setter
1 2 3 4 5 6 7 8 9 10
| computed:{ get:function(){ return this.firstName+' '+this.lastName }, set:function(newValue){ var names=newValue.split(' ') this.firstName=names[0] this.lastName=names[names.length-1] } }
|
侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。
这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
class与style绑定
绑定class
对象绑定
1 2 3 4
| <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }" ></div>
|
和如下 data:
1 2 3 4
| data: { isActive: true, hasError: false }
|
数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:
1 2 3 4 5
| <div v-bind:class="[activeClass, errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger' }
|
在数组中使用对象
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
绑定内联样式
v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
1 2 3 4 5
| <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 }
|
数组语法数组语法
v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
1 2 3 4 5
| Vue.component('blog-post',{ props:['title'], template:`<h3>{{title}}</h3>` })
|
全局注册
1 2 3
| Vue.component('my-component-name', { // ... 选项 ... })
|
局部注册
在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:
1 2 3
| var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } var ComponentC = { /* ... */ }
|
然后在 components 选项中定义你想要使用的组件:
1 2 3 4 5 6 7
| new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
|
基础组件的自动化全局注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import Vue from 'vue' import upperFirst from 'lodash/upperFirst' import camelCase from 'lodash/cameCase' const requireComponent=require.context( //其组件目录的相对路径 './components', //是否查询其子目录 false, //匹配基础组件的文件名的正则表达式 /Base[A-Z]\w+\.(vue|js)$/ ) requireComponent.keys().forEach(fileName=>{ //获取组件配置 const componentConfig=requireComponent(fileName) //获取组件的PascalCase命名 const componentName=upperFirst( cmaeCase( fileName.split('/').pop().replace(/\.\w+$/,'') ) ) //全局注册 Vue.component(componentName, //如果这个组件选项是通过'export default'导出 //那么就会优先使用'.default' //否则回退到使用模块的根 componentConfig.default||componnentConfig ) })
|
传入一个对象的所有属性
如果你想要将一个对象的所有属性都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:
1 2 3 4
| post: { id: 1, title: 'My Journey with Vue' }
|
下面的模板:
1 2 3 4 5 6 7
| <blog-post v-bind="post"></blog-post> 等价于:
<blog-post v-bind:id="post.id" v-bind:title="post.title" ></blog-post>
|
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图改变一个 prop 的情形:
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
1 2 3 4 5 6
| props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
|
这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
1 2 3 4 5 6
| props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
|
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
1 2 3 4 5
| propF:{ validator:function(value){ return ['success','danger'].indexOf(value)!==-1 } }
|
将原生事件绑定到组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 在有的时候这是很有用的,不过在你尝试监听一个类似 <input> 的非常特定的元素时,这并不是个好主意。 比如上述 <base-input> 组件可能做了如下重构,所以根元素实际上是一个 <label> 元素: Vue.component('base-input',{ inheritAttrs:false, props:['label','value'], computed:{ inputListeners:function(){ var vm=this return Object.assign({}, this.$listeners, //然后我们添加自定义监听器 //或覆写一些监听器的行为 { //这里确保组件配合'v-model'的工作 input:function(event){ vm.$emit('input',event.target.value) } } ) } }, template:` <label> {{label}} <input v-bind="$attrs" v-bind:value="value" v-on="inputListeners" > </label> ` })
|
具名参数
1 2 3 4 5 6 7 8 9 10 11
| <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
|
一个不带 name 的 出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
1 2 3 4 5 6 7 8 9 10 11 12
| <base-layout> <template v-slot:header> <h1>Here might be a page title</h1> </template>
<p>A paragraph for the main content.</p> <p>And another one.</p>
<template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
|
现在 <template>
元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template>
中的内容都会被视为默认插槽的内容。
过渡js钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <transition v-on:enter="enter"></transition> 状态过滤-把过渡放组件里 Vue.component('animated-integer',{ template:'<span>{{tweeningValue}}</span>', props:{ value:{ } }, data:function(){ }, watch:{ }, mouted:function(){ }, methods:{
}
}) <div id="example"> <animated-integer></animated-integer> </div> new Vue({ el:'#example', data:{ firstnumber:20, lastnumber:40 }, computed:{ result:function(){ } }
})
混入 var myMixin={ created:function(){ } } var Component=Vue.extend({ mixins:[myMixin] }) var component=new Component()
|
选项合并
1 数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
2 同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用
全局混入
1 2 3 4 5 6 7 8 9 10 11 12
| Vue.mixin({ created:function(){ var myOption=this.$options.myOption if(myOption){ console.log(myOption) } } })
new Vue({ myOption:'HELLO' })
|
自定义选项合并策略
//函数
Vue.config.optionMergeStrategies.myOption=function(toVal,fromVal){
//返回合并后的值
}
//或者使用与methods相同的合并策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var strategies=Vue.config.optionMergeStrategies strategies.myOption=strategies.methods
const merge=Vue.config.optionMergeStrategies.computed Vue.config.optionMergeStragies.vux=function(toVal,fromVal){ if(!toVal) return fromVal if(!fromVal) return toVal return { getters:merge(toVal.getters,fromVal.getters), state:merge(toVal.state,fromVal.state), actions:merge(toVal.actions,fromVal.actions) } }
|
自定义指令
全局指令
1 2 3 4 5 6 7
| Vue.directive('focus',{ //当被绑定元素插入到dom时 inserted:function(el){ //聚焦元素 el.focus() } })
|
注册局部指令,组件中也接受一个directives的选项
1 2 3 4 5 6 7 8
| directives:{ focus:{ inserted:function(el){ el.focus() } } } <input v-focus>
|
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
函数简写
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
1 2 3
| Vue.directive('color-swatch', function (el, binding) { el.style.backgroundColor = binding.value })
|
渲染函数
createElement 参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| createElement( // {String | Object | Function} // 一个 HTML 标签名、组件选项对象,或者 // resolve 了上述任何一种的一个 async 函数。必填项。 'div', //{object} 一个与模板中属性对应的数据对象 { }, //子级虚拟节点(Vnode),由createElement(),构建而成 //也可以使用字符串来生成‘文本虚拟节点’,可选 [ 'some word', createElment(MyComponent,{ props:{ someProp:'foobar' } }) ]
)
|
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| var getChildrenTextContext=function(children){ return children.map(function(node){ return node.children?getChildrenTextContext(node.children): node.text }).join('') }
Vue.component('anchored-heading',{ render:function(createElement){ var headingId=getChildrenTextContext(this.$slots.default) .toLowerCase() .replace(/\W+/g,'-') .replace(/(^-|-$)/,'') return createElement( 'h'+this.level, [ createElement('a',{ attrs:{ name:headingId, href:'#'+headingId } },this.$slots.default) ] ) }, props:{ level:{ type:Number, required:true } } }) //v-model props:['value'], render:function(createElement){ var self=this return createElement('input',{ domProps:{ value:self.value }, on:{ input:function(event){ self.$emit('input',event.target.value) } } }) }
|
函数式组件
之前创建的锚点标题组件是比较简单,没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。
在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
1 2 3 4 5 6 7 8 9
| Vue.component('my-component',{ functional:true, props:{ }, render:function(createElement,content){ } })
|
插件
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
添加全局方法或者属性。如: vue-custom-element
添加全局资源:指令/过滤器/过渡等。如 vue-touch
通过全局混入来添加一些组件选项。如 vue-router
添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| MyPlugin.install=function(Vue,options){ //全局方法或属性 Vue.myGlobalMethod=function(){ } //添加全局资源 Vue.directive('my-directive',{ bind(el,binding,vnode,oldVnode){ } }) //注入组件选项 Vue.mixin({ created:function(){ } }) //添加实例方法 Vue.prototype.$myMethod=function(methodOptions){ } }
|