怎样写 Vue 自定义指令?用 v-permission 做按钮权限有什么局限?
先理解原理和示例,再练习完整口述与追问。
回答要点
指令适合封装直接的 DOM 操作,Vue3 钩子为 created、beforeMount、mounted、beforeUpdate、updated、beforeUnmount、unmounted,参数为 el、binding、vnode。v-permission 在 mounted 中移除无权限元素,简单但不能真正阻止组件创建,权限变化后不能恢复,对 Tabs、表格列等由数据驱动的组件也无效;更稳妥的是 v-if 配合权限函数,并以后端校验为准。
原理拆解
指令对象的 binding 包含 value、oldValue、arg(v-xx:arg)、modifiers 与 instance。常见场景有自动聚焦、点击外部关闭、懒加载图片、拖拽、复制。需要清理的副作用(事件监听、IntersectionObserver)在 unmounted 中解绑,并通过 el 上的属性或 WeakMap 保存引用。
el.parentNode.removeChild(el) 只是把真实 DOM 摘掉,组件实例仍然存在,生命周期照常运行;之后父组件更新时,Vue 的 patch 依赖的节点已不在原位,可能出现异常。el-tabs 中的 el-tab-pane 由组件根据插槽数据渲染标签头,指令删掉 pane 的内容也去不掉标签头。用 v-if="can('order:delete')" 时组件根本不会创建,权限变化后也能响应式更新。
示例与推演
const clickOutside = {
mounted(el, binding) {
el.__clickOutside__ = event => {
if (!el.contains(event.target)) binding.value(event)
}
document.addEventListener('pointerdown', el.__clickOutside__)
},
unmounted(el) {
document.removeEventListener('pointerdown', el.__clickOutside__)
delete el.__clickOutside__
}
}
app.directive('click-outside', clickOutside)
易错点
前端按钮权限只是体验层面的隐藏,接口必须在后端鉴权;在组件上使用指令时,它作用于组件的根元素,多根组件会被忽略并警告。
参考回答可直接练习口述
自定义指令适合封装 DOM 操作,比如聚焦、点击外部、懒加载,在 mounted 绑定、unmounted 清理,参数从 binding 里取。v-permission 常见做法是在 mounted 中移除元素,但组件实例照样创建,权限变化后无法恢复,Tabs 这类由数据生成的组件也删不干净。我更倾向用 v-if 加权限函数或权限组件,并在路由和菜单层控制,最终以后端接口校验为准。