使用场景
在vue中,自定义指令可用于定义复杂的行为或公用的行为,以便于在多个组件中重复使用。
例如,如果你想在一个应用中多处使用拖拽功能,你可以定义一个v-draggable
指令,某个组件通过使用该指令来为某个元素启用拖拽功能。
另一个例子是,如果你想在一个应用中多处使用格式化日期的功能,你可以定义一个 v-date-format
指令,在组件中通过使用该指令来自动格式化日期。
总之,当你想要在多个组件中重用某个复杂的行为或逻辑时,就可以考虑使用自定义指令。
自定义指令
Vue 3 的指令系统与 Vue 2 相比有了一些改变。在 Vue 2 中,指令是一种特殊的 attribute,它们以 v- 为前缀,例如 v-if 和 v-for。
在 Vue 3 中,指令仍然可以用作 attribute,但是它们也可以作为一种新的声明式 API,以 on
开头,例如onBeforeMount
和 onBeforeUpdate
。
要封装一个自定义指令,首先需要定义一个函数,其中的第一个参数是指令的绑定值,第二个参数是一个包含指令信息的对象。
注册指令 MyComponent.vue
import { directive } from 'vue'
const MyDirective = directive((el, binding) => {
// 获取指令的值
const value = binding.value
// 在指令的钩子函数中执行逻辑
el.style.color = value
})
export default {
name: 'MyComponent',
directives: {
MyDirective,
},
}
使用方法:
<template>
//使用 attribute 形式
<div v-my-component='red'>Hello, world!</div>
//使用声明式 API 形式
<div onMyComponent='red'>Hello, world!</div>
</template>
<script>
import MyComponent from './MyComponent.vue
</script>
请注意,在声明式 API 中,指令名称的首字母会被自动转换为小写。
将自定义指令注册为组件,全局使用
指令的生命周期和组件类似,首先我们要让指令能够支持 Vue 的插件机制,所以我们需要在 install 函数内注册 lazy 指令。
在 install 方法的内部去注册 lazy 指令,并且实现了 mounted、updated、unmounted 三个钩子函数。
import { createApp } from 'vue'
const MyPlugin = {
install (app, options) {
app.directive((el, binding) => {
const value = binding.value
el.style.color = value
})
}
}
const app = createApp(App)
app.use(MyPlugin)
当然,你也可以将自定义指令的插件写的更加优雅,这样无论是可读性还是日后的维护都会非常友好
下面的代码中,我们注册了 loadingDirective 指令,并且注册了 mounted、updated、unmounted 三个钩子函数,通过 v-loading 的值来对显示效果进行切换,实现了组件内部的 loading 状态。
动态切换的 Loading 组件能够显示一个 circle 的 div 标签,通过 v-loading 指令的注册,在后续表格、表单等组件的提交状态中,加载状态就可以很方便地使用 v-loading 来实现。
const loadingDirective = {
mounted: function (el, binding, vnode) {
const mask = createComponent(Loading, {
...options,
onAfterLeave() {
el.domVisible = false
const target =
binding.modifiers.fullscreen || binding.modifiers.body
? document.body
: el
removeClass(target, 'el-loading-parent--relative')
removeClass(target, 'el-loading-parent--hidden')
}
})
el.options = options
el.instance = mask.proxy
el.mask = mask.proxy.$el
el.maskStyle = {}
binding.value && toggleLoading(el, binding)
},
updated: function (el, binding) {
el.instance.setText(el.getAttribute('element-loading-text'))
if (binding.oldValue !== binding.value) {
toggleLoading(el, binding)
}
},
unmounted: function () {
el.instance && el.instance.close()
}
}
export default {
install(app) {
// if (Vue.prototype.$isServer) return
app.directive('loading', loadingDirective)
}
}