在某些情况下,我们需要对底层DOM
进行操作,而内置的指令不能满足需求,就需要自定义指令。一个自定义指令由一个包含类似组件的生命周期的钩子的对象来定义,钩子函数会接收到指令所绑定的元素作为参数。
定义指令
常用两种方式进行自定义指令,一种是全局定义,另一种在当前组件中定义
// 局部定义
export default {name:'test',data(){return {}},directives: {focus: {bind: function(el, bindings, vnode) {/* ... */},inserted: function(el, bindings, vnode) { el.focus(); },update: function(el, bindings, vnode) { /* ... */ },componentUpdated: function(el, bindings, vnode) { /* ... */ },unbind: function(el, bindings, vnode) { /* ... */ }}},methos:{}
};
// 全局定义
const app = createApp({});
// 使 v-focus 在所有组件中都可用
app.directive("focus", {bind: function(el, bindings, vnode) {/* ... */},inserted: function(el, bindings, vnode) { el.focus(); },update: function(el, bindings, vnode) { /* ... */ },componentUpdated: function(el, bindings, vnode) { /* ... */ },unbind: function(el, bindings, vnode) { /* ... */ }
});
// 全局和局部的调用方式相同
<input v-focus>
钩子函数
钩子函数就是拦截模块渲染流程的“挂钩”,在不同的节点拦截或监听事件,以完成自定义指令的操作。五种钩子拦截的事件分别是初始化、插入节点、渲染完成,监听绑定值的变化和销毁节点。
- bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
- inserted:节点第一次插入到父节点(页面)中会触发,只会触发这一次.
- update:当前元素渲染完成,绑定的值发生改变的时候触发
- componentUpdated:当前元素所在的区域全部渲染完成,绑定的额值发生改变时触发。
- unbind:只调用一次,指令与元素解绑时调用。(比如离开页面或销毁组件等)
钩子参数
钩子参数即绑定到钩子函数上的参数
-
el:指令绑定到的元素。这可以用于直接操作
DOM
。 -
binding:一个对象,包含以下属性。
- value:传递给指令的值。例如在
v-my-directive="1 + 1"
中,值是 2。 - oldValue:修改前的值,仅在
beforeUpdate
和updated
中生效。无论是否修改都可以访问。 - arg:传递给指令的参数 (如果有的话)。例如在
v-my-directive:foo
中,参数是"foo"
。 - modifiers:一个包含修饰符的对象 (如果有的话)。例如在
v-my-directive.foo.bar
中,修饰符对象是{ foo: true, bar: true }
。 - instance:使用该指令的组件实例。
- value:传递给指令的值。例如在
-
dir:指令的定义对象。
-
vnode:代表绑定元素的底层 VNode。
-
prevNode:代表之前的渲染中指令所绑定元素的
VNode
。仅在beforeUpdate
和updated
钩子中可用。
传递参数
传递钩子参数的方式有如下几种
- 实例化一个指令,但没有参数 v-xxx。例如上面提到的
v-focus
- 传递值的指令 v-xxx=“value”。value 值是变量,一般会绑定到
bingdings
的value
上 - 传递字符串的指令 v-xxx=“‘string’”,例如
v-html="'<p>Content</p>'"
- 传递参数的指令 v-xxx:arg1=value1,值
arg1
和value1
分别绑定到bingdings
的arg
和value
- 传递动态参数的指令 v-xxx:[arg]=value1,变量
arg
可以实现动态指令参数 - 使用修饰符(modifier)的指令 v-xxx:arg.m1=“value”,
m1
会被绑定到bingdings
的modifier
上。允许多个修饰符用逗号连接绑定。例如a.b.c
对应的是{a:true,b:true,c:true}
函数简写
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('color-swatch', function (el, binding) { el.style.backgroundColor = binding.value
})
对象字面量
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript
表达式。
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
render函数使用自定义指令
在vue当时我们使用指令一般是如下的方式
<template><div><el-button type="primary" v-norepeat.disabled="{time:3000}" @click="doUpdateItem">保存</el-button></div>
</template>
但是我们想在render
函数里面写上面的指令,改怎么写呢?
首先想到的就是下面的写法,但是报错了
1.使用创建虚拟节点的函数
export default {render(h) {const time = 3000; // 这里是 v-norepeat.disabled 的值return h('div',[h('el-button',{props: {type: 'primary'},directives: [{name: 'norepeat',value: {time }, // value 是指令绑定的值arg: null, // arg 是指令的参数modifiers: {disabled: true // modifiers 是指令的修饰符}}],on: {click: this.doUpdateItem}},'保存')]);},methods: {doUpdateItem() {console.log('Button clicked!');// 这里放置点击后的操作}}
};
- jsx写法
export default {render() {const time = 3000; // 这里是 v-norepeat.disabled 的值const directives= [{name: 'norepeat',value: {time}, // value 是指令绑定的值arg: null, // arg 是指令的参数modifiers: {disabled: true // modifiers 是指令的修饰符}}]return (<div><el-button type='primary' {...{directives}} onClick={this.doUpdateItem}>保存</el-button></div>)},methods: {doUpdateItem() {console.log('Button clicked!');// 这里放置点击后的操作}}
};