Vue3拓展:自定义权限指令

一、实验目标

  1. 掌握 Vue3 自定义指令的 4 个核心生命周期钩子(created/mounted/updated/unmounted)及执行时机

  2. 独立封装1个高频业务指令(v-permission 权限控制),实现一次封装、全局复用

  3. 熟练运用指令、动态参数、修饰符扩展指令功能,适配复杂业务场景

  4. 掌握自定义指令的 全局注册、局部注册 方式,理解 Tree-Shaking 兼容方案,避免打包冗余

  5. 解决实际开发中 "重复 DOM 操作、业务逻辑散落在组件" 的痛点,提升代码复用性和可维护性

二、实验步骤

1. 指令 :v-permission(权限控制)

需求:根据用户角色控制元素显示 / 隐藏(支持多个角色、反向权限)。

(1)封装实现(src/directives/permission.ts

javascript 复制代码
import type { Directive, DirectiveBinding } from 'vue'

// 模拟全局用户角色(实际从 Pinia/本地存储获取)
const userRoles = ['admin', 'editor']

const permissionDirective: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    const { value: requiredRoles, modifiers } = binding
    // 1. 若指令无值,直接隐藏
    if (!requiredRoles || !Array.isArray(requiredRoles)) {
      el.style.display = 'none'
      return
    }
    // 2. 判断用户是否拥有所需角色(some:满足一个即可)
    const hasPermission = requiredRoles.some(role => userRoles.includes(role))
    // 3. modifiers.invert:反向权限(有角色则隐藏)
    if (modifiers.invert ? hasPermission : !hasPermission) {
      el.style.display = 'none'
    }
  }
}

export default permissionDirective

(2)全局注册

src/directives/index.ts 中统一导出指令:

javascript 复制代码
import type { App } from 'vue'
import permission from './permission'

// 指令映射表(key:指令名,value:指令实现)
const directives = {
  permission
}

// 全局注册所有指令
export const registerDirectives = (app: App) => {
  Object.entries(directives).forEach(([name, directive]) => {
    app.directive(name, directive)
  })
}

(3)在 src/main.ts 中调用注册:

javascript 复制代码
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import { registerDirectives } from './directives'


// 1. 创建 Pinia 实例(TS 自动推断为 Pinia 类型)
const pinia = createPinia()
// 2. 安装持久化插件
pinia.use(piniaPluginPersistedstate)
// 3. 全局注册 Pinia(Vue 实例类型自动适配)
const app = createApp(App).use(pinia)
registerDirectives(app)
app.mount('#app')

(3) 实验结果

(4)实验说明

permission.ts中的模拟部分可以修改测试

bash 复制代码
// 模拟全局用户角色(实际从 Pinia/本地存储获取)

const userRoles = ['admin', 'editor']

HelloWorld.vue中的逻辑也可以修改测试

bash 复制代码
<template>
  <!-- 只有 admin/editor 可见 -->
  <button v-permission="['admin', 'editor']">编辑按钮</button>
  <!-- 反向权限:admin 不可见 -->
  <button v-permission.invert="['admin']">普通用户按钮</button>
</template>

三、核心原理

(一)自定义指令的本质与工作流程

Vue3 自定义指令的核心是 "封装 DOM 操作逻辑",本质是一个包含生命周期钩子的对象。其工作流程如下:

  1. 注册阶段 :通过 app.directive(name, directive) 或局部注册,Vue 会将指令与指令名关联;

  2. 解析阶段 :模板编译时,Vue 识别 v-xxx 指令,将其与对应的 DOM 元素绑定;

  3. 执行阶段 :DOM 元素触发对应生命周期(如挂载、更新、卸载)时,Vue 调用指令的对应钩子函数,传入 el(DOM 元素)和 binding(参数对象);

  4. 销毁阶段 :元素卸载时,调用 unmounted 钩子,清理事件监听、定时器等,避免内存泄漏。

(二)关键 API 与参数解析

  1. 核心参数 DirectiveBinding

指令钩子的第二个参数 binding 包含指令的所有配置信息,类型定义如下:

javascript 复制代码
interface DirectiveBinding<T = any> {
  value: T;          // 指令值(如 v-debounce="handleClick" 中的 handleClick)
  oldValue: T | null; // 上一次的指令值(仅 updated 钩子可用)
  arg?: string;      // 动态参数(如 v-debounce:1000 中的 1000)
  modifiers: Record<string, boolean>; // 修饰符(如 v-permission.invert 中的 { invert: true })
  instance: ComponentPublicInstance | null; // 指令所在的组件实例
  dir: Directive;    // 指令本身的定义对象
}
  1. 生命周期钩子的设计逻辑
  • created:用于初始化准备(如存储初始状态),此时 DOM 尚未挂载,无法操作 el 的样式 / 事件;

  • mounted:核心钩子,DOM 已挂载,可安全执行 DOM 操作(如绑定事件、设置样式);

  • updated:组件更新时触发,用于同步指令状态(如指令值变化后更新 DOM);

  • unmounted:用于资源清理(如移除事件监听、销毁定时器、停止观察者),避免内存泄漏。

相关推荐
EndingCoder1 分钟前
函数基础:参数和返回类型
linux·前端·ubuntu·typescript
码客前端7 分钟前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛7 分钟前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
工藤学编程20 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保20 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫21 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
内存不泄露27 分钟前
基于Spring Boot和Vue 3的智能心理健康咨询平台设计与实现
vue.js·spring boot·后端
欧阳天风29 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder33 分钟前
箭头函数和 this 绑定
linux·前端·javascript·typescript
郑州光合科技余经理33 分钟前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活