在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,},
}
使用方法:
//使用 attribute 形式Hello, world!//使用声明式 API 形式Hello, world!
请注意,在声明式 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.valueel.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 = falseconst target =binding.modifiers.fullscreen || binding.modifiers.body? document.body: elremoveClass(target, 'el-loading-parent--relative')removeClass(target, 'el-loading-parent--hidden')}})el.options = optionsel.instance = mask.proxyel.mask = mask.proxy.$elel.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) returnapp.directive('loading', loadingDirective)}
}