Vue3的指令

Directive(自定义指令) 是 Vue 提供的一种扩展 DOM 行为的机制。

在模板中,Vue 本身提供了一些内置指令,比如:

v-if、v-for、v-model、v-show、v-bind

这些都是“指令”。

除了内置指令,你也可以自己注册自定义指令,比如 v-permissionv-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),并将它们映射为指令对象。

内部执行流程大致如下:

  1. 模板解析时,识别到 v-xxx 指令;

  2. Vue 会查找注册过的 xxx 指令;

  3. 如果找到了,Vue 会在元素生命周期内(如 mountedupdated)调用对应的钩子;

  4. 每次元素更新或卸载时,也会调用相应的钩子,处理清理逻辑。

例子

权限指令

有权限则可以看到该元素,没有权限则移除该元素

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>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值