3.3 指令的高级选项
Vue.js指令定义对象中除了钩子函数外,还有一些其他的选项,我们将在本节中对其做逐个的讲述。
3.3.1 params
定义对象中可以接受一个params数组,Vue.js编译器将自动提取自定义指令绑定元素上的这些属性。例如:
<div v-my-advance-directive a="paramA"></div> Vue.directive('my-advance-directive', { params : ['a'], bind : function() { console.log('params', this.params); } });
除了直接传入数值外,params支持绑定动态数据,并且可以设定一个watcher监听,当数据变化时,会调用这个回调函数。例如:
<div v-my-advance-directive v-bind:a="a"></div> // 当然也可以简写成 <div v-my-advance-directive :a="a"></div> Vue.directive('my-advance-directive', { params : ['a'], paramWatchers : { a : function(val, oldVal) { console.log('watcher: ', val, oldVal) } }, bind : function() { console.log('params', this.params); } }); var vm = new Vue({ el : '#app', data : { a : 'dynamitc data' } });
3.3.2 deep
当自定义指令作用于一个对象上时,我们可以使用deep选项来监听对象内部发生的变化。例如:
<div v-my-deep-directive="obj"></div> <div v-my-nodeep-directive="obj"></div> Vue.directive('my-deep-directive', { deep : true, update : function(newValue, oldValue) { console.log('deep', newValue.a.b); } }); Vue.directive('my-nodeep-directive', { update : function(newValue, oldValue) { console.log('deep', newValue.a.b); } }); var vm = new Vue({ el : '#app', data : { obj : { a : { b : 'inner' } } } })
运行后,在控制台中输入vm.obj.a.b = 'inner changed',只有my-deep-directive调用了update函数,输出了改变后的值。
Vue.js 2.0中废弃了该选项。
3.3.3 twoWay
在自定义指令中,如果需要向Vue实例写回数据,就需要在定义对象中使用twoWay:true,这样可以在指令中使用this.set(value)来写回数据。
<input type="text" v-my-twoway-directive="param" / > Vue.directive('my-twoway-directive', { twoWay : true, bind : function() { this.handler = function () { console.log('value changed: ', this.el.value); this.set(this.el.value) }.bind(this) this.el.addEventListener('input', this.handler) }, unbind: function () { this.el.removeEventListener('input', this.handler) } }); var vm = new Vue({ el : '#app', data : { param : 'first', } });
此时在input中输入文字,然后在控制台中输入vm.param即可观察到实例的param属性已被改变。
需要注意的是,如果没有设定twoWay:true,就在自定义指令中调用this.set(), Vue.js会抛出异常。
3.3.4 acceptStatement
选项acceptStatement:true可以允许自定义指令接受内联语句,同时update函数接收的值是一个函数,在调用该函数时,它将在所属实例作用域内运行。
<div v-my-directive="i++"></div> Vue.directive('my-directive', { acceptStatement: true, update: function (fn) { } }) var vm = new Vue({ el : '#app', data : { i : 0 } });
如果在update函数中,运行fn(),则会执行内联语句i++,此时vm.i = 1。但更改vm.i并不会触发update函数。
需要当心的是,如果此时没有设定acceptStatement: true,该指令会陷入一个死循环中。v-my-statement-directive接受到i的值每次都在变化,会重复调用update函数,最终导致Vue.js抛出异常。
3.3.5 terminal
选项terminal的作用是阻止Vue.js遍历这个元素及其内部元素,并由该指令本身去编译绑定元素及其内部元素。内置的指令v-if和v-for都是terminal指令。
使用terminal选项是一个相对较为复杂的过程,你需要对Vue.js的编译过程有一定的了解,这里借助官网的一个例子来大致说明如何使用terminal。
<div id="modal"></div> ... <div v-inject:modal> <h1>header</h1> <p>body</p> <p>footer</p> </div> var FragmentFactory = Vue.FragmentFactory // Vue.js全局API,用来创造fragment 的工厂函数,fragment中包含了具体的scope和DOM元素,可以看成一个独立的组件或者实例。 var remove = Vue.util.remove // Vue.js工具类函数,移除DOM元素 var createAnchor = Vue.util.createAnchor // 创建锚点,锚点在debug模式下是 注释节点,非debug模式下是文本节点,主要作用是标记dom元素的插入和移除 Vue.directive('inject', { terminal: true, bind: function () { var container = document.getElementById(this.arg) // 获取需要注入到的DOM元素 this.anchor = createAnchor('v-inject') // 创建v-inject锚点 container.appendChild(this.anchor) // 锚点挂载到注入节点中 remove(this.el) // 移除指令绑定的元素 var factory = new FragmentFactory(this.vm, this.el) // 创建fragment this.frag = factory.create(this._host, this._scope, this._frag) // this._host用于表示存在内容分发时的父组件 // this._scope用于表示存在v-for时的作用域 // this._frag用于表示该指令的父fragment this.frag.before(this.anchor) }, unbind: function () { this.frag.remove() remove(this.anchor) } })
最终我们得到的结果是:
3.3.6 priority
选项priority即为指定指令的优先级。普通指令默认是1000, termial指令默认为2000。同一元素上优先级高的指令会比其他指令处理得早一些,相同优先级则按出现顺序依次处理。以下为内置指令优先级顺序:
export const ON = 700 export const MODEL = 800 export const BIND = 850 export const TRANSITION = 1100 export const EL = 1500 export const COMPONENT = 1500 export const PARTIAL = 1750 export const IF = 2100 export const FOR = 2200 export const SLOT = 2300