本章概要
- 自定义指令
- 钩子函数
- 动态指令参数
- 函数简写
- 对象字面量
5.2 自定义指令
5.2.1 自定义指令的注册
自定义指令需要注册后才能使用,Vue 提供了两种注册方式:全局注册和本地注册。
全局注册使用应用程序实例的 directive() 方法注册一个全局自定义指令,该方法接受两个参数,第一个参数是指令的名字;第二个参数是一个定义对象或函数对象,将指令要实现的功能在这个对象中定义。
语法形式如下:
app.directive(name,[definition])
例如,要编写一个让元素自动获取焦点的全局指令。如下:
const app = Vue.createApp({});
app.directive('focus',{
//...
})
本地注册是在组件实例的选项对象中使用 directive 选项进行注册。如下:
directives:{
focus:{
//指令定义
mounted(el) {
el.focus;
}
}
}
然后在模板中就可以在任何元素上使用 v-focus 指令了,如下:
<input v-focus>
5.2.2 钩子函数
自定义指令的功能是在定义对象中实现的,而定义对象则是由钩子函数组成的,Vue 提供了下面几个钩子函数,这些钩子函数都是可选的。
- beforeMount :当指令第一次绑定到元素并且在挂载父组件之前调用。指令如果需要一些一次性的初始化设置,可以放到这个钩子函数中
- mounted:在挂载绑定元素的父组件时调用
- beforeUpdate:指令所在组件的 VNode 更新之前调用
- updated:指令所在组件的 VNode 及其子组件的 Vnode 全部更新后调用
- beforeUnmount:在卸载绑定元素的父组件之前调用
- unmounted:只调用一次,指令与元素解绑且父组件已卸载时调用
根据指令要实现功能的不同,选择相应的钩子函数来编写代码。例如 5.2.1小节中的 v-focus 指令,我们希望在绑定元素被挂载到 DOM 时就自动获得焦点,那么就可以在 mounted 钩子函数中编写自动聚焦的代码。如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<input v-focus>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({});
// 注册一个全局自定义指令
app.directive('focus', {
// 当绑定元素被挂载到DOM中时
mounted(el) {
// 聚焦元素
el.focus()
}
})
app.mount('#app')
</script>
</body>
</html>
浏览器中的显示效果如下:
指令的钩子函数可以带有一些参数,例5-17中 mounted 函数的 el 参数就是其中之一,这些参数如下:
- el:指令所绑定的元素,可以用来直接操作 DOM
- binding:一个对象,包含以下属性
- instance:使用指令的组件的实例
- value:传给指令的值。例如,在 v-my-directive="1+1"中,value 的值为 2
- oldValue:前一个值,仅在 beforeUpdate 和 updated 钩子中可用。无论值是否改变都可用
- arg:传给指令的参数,可选。例如,在 v-my-directive:foo 中,arg 的值为 foo
- modifiers:一个包含修饰符的对象。例如,在 v-my-directive.foo.bar 中,modifiers 对象的值为 {foo:true,bar:true}
- dir:注册指令时作为参数传递的对象。如下面注册指令的代码
app.directive('focus',{
mounted(el) {
el.focus()
}
})
dir 是下面的对象
{
mounted(el) {
el.focus()
}
}
- vnode:Vue编译生成的虚拟节点
- oldVnode:上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用
需要注意的是,除了 el 参数外,其它参数都应该是只读,切勿进行修改。
编写一个自定义指令,在其钩子函数中将各个参数输出。如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<p v-demo:foo.a.b="message"></p>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: '斗罗大陆'
}
}
})
app.directive('demo', {
mounted (el, binding, vnode) {
let s = JSON.stringify
el.innerHTML =
'instance: ' + s(binding.instance) + '<br>' +
'value: ' + s(binding.value) + '<br>' +
'argument: ' + s(binding.arg) + '<br>' +
'modifiers: ' + s(binding.modifiers) + '<br>' +
'vnode keys: ' + Object.keys(vnode).join(', ')
}
})
app.mount('#app')
</script>
</body>
</html>
将 bind 钩子函数的所有参数信息取出来拼接成字符串,赋值给 div 元素的 innerHTML 属性。渲染结果如下:
5.2.3 动态指令参数
自定义的指令也可以使用动态参数,例如,在 v-mydirective:[argument]=“value” 中,argument 参数可以根据组件实例数据进行更新,这使自定义指令可以在应用中被灵活运用。
例5-19
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<!--直接给出指令的参数-->
<p v-pin:top="100">
无限恐怖
</p>
<!--使用动态参数-->
<p v-pin:[direction]="100">
恐怖复苏
</p>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
direction: 'left'
}
}
})
app.directive('pin', {
beforeMount(el, binding, vnode) {
el.style.position = 'fixed';
let s = binding.arg || 'left';
el.style[s] = binding.value + 'px'
}
})
app.mount('#app')
</script>
</body>
</html>
浏览器的显示结果如下:
5.2.4 函数简写
如果自定义指令在 mounted 和 updated 钩子函数中的行为一致,且只需要用到这两个钩子函数,那么可以在注册时传递一个函数对象作为参数。如下:
app.directive('color-swatch',(el,binding) =>{
el.style.backgroundColor = binding.value
})
5.2.5 对象字面量
如果指令需要多个值,则可以传入一个 JavaScript 对象字面量。但要记住的是,指令可以接受所有合法的 JavaScript 表达式。
<div v-demo="{color:'white',text:'hello'}"></div>
app.directive('demo',(el,binding)=>{
console.log(binding.value.color) //white
console.log(binding.value.text) //hello
})