Vue系列 | 自定义指令

  • 操作底层DOM

代码复用和抽象的主要形式是组件,有时候需要自定义指令来操作底层DOM。
让我们看一个例子:

1
2
// HTML
<input v-focus/>

1
2
3
4
5
6
7
8
9
10
// 在组件的JS代码中自定义指令focus
export default {
directives: {
focus: {
// directive definition
inserted: function (el) {
el.focus()
}
}
}

以上代码的作用是进入页面的时候输入框会自动获取焦点,即处于输入状态,注意,在使用的时候,Vue会自动在我们的指令前面加上前缀v-,所以使用是指令表示为v-focus

钩子函数与钩子参数

与组件的操作过程类似,钩子用于处理底层DOM,所以天生携带组件的生命体征,即我们也会像操作组件一样来处理我们的指令,找到对应的时机:包含绑定指令,插入元素,数据更新,组件更新和解绑指令,这些表现为函数形式,操作我们的数据,表现为在相应的函数里传递和操作参数。

具体函数如下:
bind:顾名思义,绑定我们定义的指令到元素上,而且整个生命周期中只会绑定一次,我们可以在这里做一些一次性的创建工作。
inserted:当绑定元素已经插入到它的父节点的时候调用(它只保证父节点存在,不一定在文档中)。
update:包含组队的VNode被更新的时候调用,但是可能会在子组件更新之前调用。指令的值可能改变也可能不改变。
componentUpdated:包含组件的VNode和VNodes的子组件更新后调用。
unbind:仅调用一次,和bind相对,自定义指令从元素解绑时调用。

有了这些函数,我们就可以在对应的阶段执行一些处理DOM的操作。要操作DOM我们还需要这些函数传递的参数,我们通过一个例子来认识这些参数:

1
2
// HTML
<div v-demo:foo.a.b="message"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// JS
export default {
directives: {
demo: {
bind: function (el, binding, vnode) {
var s = JSON.stringify
el.innerHTML =
'name:' + s(binding.name) + '<br>' +
'value:' + s(binding.value) + '<br>' +
'expression:' + s(binding.expression) + '<br>' +
'argument:' + s(binding.arg) + '<br>' +
'modifiers:' + s(binding.modifiers) + '<br>' +
'vnode keys:' + Object.keys(vnode).join(', ')
}
}
},
data() {
return {
message: 'hello!',

}
}
}

打印结果:
name:”demo”
value:”hello!”
expression:”message”
argument:”foo”
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


结合上面的例子,我们来认识这些参数:
el:指令绑定的元素,可以用它来直接操作DOM
vnode:通过Vue的编译器产生的虚拟DOM节点
oldVnode:之前的虚拟节点,仅在updatecomponentUpdated函数可用

binding:一个对象,包含如下属性:

name:指令名称,没有-v前缀,如”demo”
value: 传递给指令的值,如”hello!”
expression:绑定的表达式,用字符串表示,如上:”message”
arg:传递给指令的参数,如”foo”
modifiers:一个包含修饰符的对象,如上的修饰符对象是{“a”:true,”b”:true}
oldValue:之前的值,仅在updatecomponentUpdated函数可用,无论值是否改变它都是可用的。

注意
除了el,其他的要素不要修改,作为只读属性。

动态指令参数

1
2
3
// HTML
<p>向下滚动页面</p>
<p v-pin:[direction]="200">让我从页面的200像素处开始</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// JS
export default {
directives: {
pin: {
bind: function (el, binding) {
el.style.position = 'fixed'
var s = (binding.arg == 'left' ? 'left':'top')
el.style[s] = binding.value + 'px'
}
}
},
data() {
return {
direction: 'top'

}
}
}

Alt text

这样我们就可以根据direction的值来做页面的动态调整了。

对象字面值

可以使用JavaScript的字符串给指令传递对象字面值,例如:

1
2
// HTML
<div v-demo="{color: 'white', text: 'hello'}"></div>

1
2
3
4
5
6
7
8
9
10
11
// JS
export default {
directives: {
demo: {
bind: function (el, binding) {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello"
}
}
},
}

参考:
自定义指令