前面介绍了许多 Vue 内置的指令,比如 v-if、v-show等,这些丰富的指令能满足我们绝大部分的业务需求,不过在需要一些特殊功能时,我们仍然希望对 DOM 进行底层的操作,这时就要用到自定义指令。
基本用法
自定义指令的注册方法和组件很像,也分全局注册和局部注册,比如注册一个 v-focus 的指令,用于在 <input>、<textarea> 元素初始化时自动获取焦点,两种写法分别是:
//全局注册
Vue.directive('focus', {
//指令选项
});
// 局部注册
var app = new Vue({
el: '#app',
directives: {
focus: {
//指令选项
}
}
})
上面只是注册了自定义指令 v-focus,还没有实现具体功能,下面具体介绍自定义指令的各个选项。
自定义指令的选项是由几个钩子函数组成的,每个都是可选的。
- bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时只执行一次的初始化操作。
- inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
- update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值可以忽略不必要的模板更新。
- componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
- unbind:只调用一次,指令与元素解绑时调用。
可以根据需求在不同的钩子函数内完成逻辑代码,例如上面的 v-focus,我们希望在元素插入父节点时就调用,那用到的最好是 inserted。例如:
<div id="app">
<input type="text" v-focus>
</div>
<script>
Vue.directive('focus', {
inserted: function (el) {
el.focus();
}
})
var app = new Vue({
el: '#app'
})
</script>
每个钩子函数都有几个参数可选,比如上面我们用到了 el。它们的含义如下:
- el:指令所绑定的元素,可以用来直接操作 DOM。
- binding:一个对象,包含以下属性:
- name:指令名,不包括 v- 前缀。
- value:指令的绑定值,例如 v-my-directive=“1 + 1”,value 的值是2。
- oldValue:指令绑定的前一个值,仅在 update 和 componentupdated 钩子中可用。无论值是否改变都可以。
- expression:绑定值的字符串形式,例如 v-my-directive=“1 + 1”,expression 的值是“1 + 1”。
- arg:传给指令的参数。例如 v-my-directive: foo,arg 的值是 foo。
- modifiers:一个包含修饰符的对象。例如 v-my-directive.foo.bar,修饰符对象 modifiers 的值是 { foo: true, bar: true }。
- vnode:Vue编译生成的虚拟节点,在进阶中介绍。
- oldVnode:上一个虚拟节点仅在 update 和 componentupdated 钩子中可用。
下面是结合了以上参数的一个具体样例,代码如下:
<div id="app">
<div v-test:msg.a.b="message"></div>
</div>
<script>
Vue.directive('test', {
bind: function (el, binding, vnode) {
var keys = [];
for(var i in vnode) {
keys.push(i);
}
el.innerHTML =
'name:' + binding.name + '<br>' +
'value:' + binding.value + '<br>' +
'expression:' + binding.expression + '<br>' +
'argument:' + binding.arg + '<br>' +
'modifiers:' + JSON.stringify(binding.modifiers) + '<br>' +
'vnode keys:' + keys.join(',')
}
})
var app = new Vue({
el: '#app',
data: {
message: 'some text'
}
})
</script>
执行后结果为:
name:test
value:some text
expression:message
argument:msg
modifiers:{"a":true,"b":true}
vnode keys:tag,data,children,text,elm,ns,context,fnContext,fnOptions,fnScopeId,key,componentOptions,componentInstance,parent,raw,isStatic,isRootInsert,isComment,isCloned,isOnce,asyncFactory,asyncMeta,isAsyncPlaceholder
在大多数场景,我们会在 bind 钩子里绑定一些事件,比如在 document 上用 addEventListrener 绑定,在unbind 里用 removeEventListener 解绑,比较典型的示例就是让这个元素随着鼠标拖拽。
如果需要多个值,自定义指令也可以传入一个 JavaScript 对象字面量,只要是合法类型的 JavaScript 表达式都是可以的。例如:
<div id="app">
<div v-test="{msg: 'hello', name: 'jj'}"></div>
</div>
<script>
Vue.directive('test', {
bind: function (el, binding, vnode) {
console.log(binding.value.msg);
console.log(binding.value.name);
}
})
var app = new Vue({
el: '#app',
})
</script>