自定义指令
简介
除了核心中包含的默认指令集(如 v-model
或 v-show
),Vue 还允许您注册自己的自定义指令。
我们在Vue中介绍了两种代码复用形式:组件和组合式。组件是主要构建块,而组合式则专注于复用有状态的逻辑。自定义指令另一方面,主要是为了复用在普通元素上涉及低级DOM访问的逻辑。
自定义指令被定义为一个包含类似组件的生命周期钩子的对象。这些钩子接收指令绑定到的元素。以下是一个示例指令,当Vue将元素插入DOM时,它将聚焦输入框:
vue
<script setup>
// enables v-highlight in templates
const vHighlight = {
mounted: (el) => {
el.classList.add('is-highlight')
}
}
</script>
<template>
<p v-highlight>This sentence is important!</p>
</template>
这句话很重要!
在 `<script setup>
` 中,任何以 v
前缀开头的 camelCase 变量都可以用作自定义指令。在上面的例子中,vHighlight
可以在模板中以 v-highlight
的形式使用。
如果不使用 `<script setup>
`,则可以使用 directives
选项来注册自定义指令。
js
export default {
setup() {
/*...*/
},
directives: {
// enables v-highlight in template
highlight: {
/* ... */
}
}
}
通常在应用级别全局注册自定义指令也很常见。
js
const app = createApp({})
// make v-highlight usable in all components
app.directive('highlight', {
/* ... */
})
何时使用自定义指令
只有在需要通过直接 DOM 操作才能实现所需功能时,才应使用自定义指令。
此类的一个常见例子是 v-focus
自定义指令,可以将元素聚焦。
vue
<script setup>
// enables v-focus in templates
const vFocus = {
mounted: (el) => el.focus()
}
</script>
<template>
<input v-focus />
</template>
此指令比 autofocus
属性更有用,因为它不仅在工作页面加载时有效,当元素被 Vue 动态插入时也有效!
当可能时,推荐使用带有内置指令(如 v-bind
)的声明式模板,因为它们更高效且对服务器端渲染友好。
指令钩子
指令定义对象可以提供几个钩子函数(全部为可选)。
js
const myDirective = {
// called before bound element's attributes
// or event listeners are applied
created(el, binding, vnode) {
// see below for details on arguments
},
// called right before the element is inserted into the DOM.
beforeMount(el, binding, vnode) {},
// called when the bound element's parent component
// and all its children are mounted.
mounted(el, binding, vnode) {},
// called before the parent component is updated
beforeUpdate(el, binding, vnode, prevVnode) {},
// called after the parent component and
// all of its children have updated
updated(el, binding, vnode, prevVnode) {},
// called before the parent component is unmounted
beforeUnmount(el, binding, vnode) {},
// called when the parent component is unmounted
unmounted(el, binding, vnode) {}
}
钩子参数
指令钩子会传递这些参数。
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
:使用指令的组件实例。dir
:指令定义对象。
vnode
:表示绑定元素的底层 VNode。prevVnode
:上一次渲染中表示绑定元素的 VNode。仅在beforeUpdate
和updated
钩子中可用。
作为一个例子,考虑以下指令使用
template
<div v-example:foo.bar="baz">
binding
参数将是一个具有以下形状的对象
js
{
arg: 'foo',
modifiers: { bar: true },
value: /* value of `baz` */,
oldValue: /* value of `baz` from previous update */
}
与内置指令类似,自定义指令的参数可以是动态的。例如
template
<div v-example:[arg]="value"></div>
这里,指令参数将根据组件状态中的 arg
属性进行响应式更新。
注意
除了 el
之外,您应该将这些参数视为只读的,并且永远不要修改它们。如果您需要在钩子之间共享信息,建议通过元素的 dataset 来做。
函数简写
自定义指令通常对 mounted
和 updated
有相同的行为,不需要其他钩子。在这种情况下,我们可以将指令定义为一个函数
template
<div v-color="color"></div>
js
app.directive('color', (el, binding) => {
// this will be called for both `mounted` and `updated`
el.style.color = binding.value
})
对象字面量
如果你的指令需要多个值,你也可以传递一个JavaScript对象字面量。记住,指令可以接受任何有效的JavaScript表达式。
template
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
js
app.directive('demo', (el, binding) => {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
在组件中的用法
不推荐
不建议在组件上使用自定义指令。当组件有多个根节点时,可能会出现意外的行为。
在组件上使用时,自定义指令始终应用于组件的根节点,类似于 穿透属性。
template
<MyComponent v-demo="test" />
template
<!-- template of MyComponent -->
<div> <!-- v-demo directive will be applied here -->
<span>My component content</span>
</div>
请注意,组件可能具有多个根节点。当应用于多根组件时,指令将被忽略并抛出警告。与属性不同,指令不能通过v-bind="$attrs"
传递到不同的元素。