Directive(自定义指令) 是 Vue 提供的一种扩展 DOM 行为的机制。
在模板中,Vue 本身提供了一些内置指令,比如:
v-if、v-for、v-model、v-show、v-bind
这些都是“指令”。
除了内置指令,你也可以自己注册自定义指令,比如 v-permission、v-focus 等,来在元素挂载时添加行为。
主要用于操作原生 DOM,适合处理组件层面不方便做的“底层交互”逻辑,比如:
| 用途 | 示例 |
|---|---|
| 权限控制 | v-permission="['admin']":没权限就移除按钮 |
| 自动聚焦 | v-focus:页面加载时自动聚焦输入框 |
| 拖拽功能 | v-drag:实现拖拽元素 |
| 条形码/二维码扫码 | v-scan:绑定扫码监听事件 |
| 动态样式/类名添加 | v-highlight:高亮代码块 |
一个自定义指令对象可以包含以下 生命周期钩子:
const myDirective = {
created(el, binding, vnode, prevVnode) {
// 元素创建时调用(非必须)
},
beforeMount(el, binding, vnode, prevVnode) {
// 绑定元素插入到 DOM 前调用
},
mounted(el, binding, vnode, prevVnode) {
// 绑定元素插入到 DOM 后调用
},
beforeUpdate(el, binding, vnode, prevVnode) {
// VNode 更新前调用
},
updated(el, binding, vnode, prevVnode) {
// VNode 更新后调用
},
beforeUnmount(el, binding, vnode, prevVnode) {
// 卸载前调用
},
unmounted(el, binding, vnode, prevVnode) {
// 卸载后调用
}
}
参数解释:
-
el:绑定的 DOM 元素。 -
binding:包含传递给指令的值。 -
vnode:当前虚拟节点。 -
prevVnode:上一个虚拟节点(仅在update系列钩子中有)。
示例:
const focus = {
mounted(el: HTMLElement) {
el.focus()
}
}
注册后在模板中使用:
<input v-focus />
全局注册(通常在 main.ts):
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.directive('focus', {
mounted(el) {
el.focus()
}
})
局部注册(组件中):
export default {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}
底层实现机制简要说明
Vue 在编译模板时(即 .vue 文件被编译为 render 函数),会解析出模板中用到的所有指令(例如 v-permission),并将它们映射为指令对象。
内部执行流程大致如下:
-
模板解析时,识别到
v-xxx指令; -
Vue 会查找注册过的
xxx指令; -
如果找到了,Vue 会在元素生命周期内(如
mounted、updated)调用对应的钩子; -
每次元素更新或卸载时,也会调用相应的钩子,处理清理逻辑。
例子
权限指令
有权限则可以看到该元素,没有权限则移除该元素
import { DirectiveBinding } from 'vue'
import { useUserStore } from '@/store'
// 是否拥有特定权限
function checkPermission(el: HTMLElement, binding: DirectiveBinding) {
const { value } = binding
const userStore = useUserStore()
const { permissions } = userStore
const allPermission = '*:*:*'
if (Array.isArray(value) && value.length > 0) {
const permissionFlag = value
const hasPermission = permissions.some((permission) => {
return permission === allPermission || permissionFlag.includes(permission)
})
if (!hasPermission && el.parentNode) {
el.parentNode.removeChild(el)
}
} else {
throw new Error(
`need permissions! Like v-permission="['system:user:list']"`
)
}
}
export default {
mounted(el: HTMLElement, binding: DirectiveBinding) {
checkPermission(el, binding)
},
updated(el: HTMLElement, binding: DirectiveBinding) {
checkPermission(el, binding)
},
}
全局注册
import { App } from 'vue'
import permission from './permission'
export default {
install(Vue: App) {
Vue.directive('permission', permission)
Vue.directive('role', role)
Vue.directive('scan', scanCode)
},
}
使用
<a-button
v-permission="[CaseLibraryButtonEnum.CASE_LIBRARY_ADD]"
type="primary"
@click="handleAdd"
>
<template #icon>
<icon-plus />
</template>
{{ $t('common.add') }}
</a-button>
1886

被折叠的 条评论
为什么被折叠?



