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:用于资源清理(如移除事件监听、销毁定时器、停止观察者),避免内存泄漏。

相关推荐
玉宇夕落24 分钟前
React 父子组件通信 Todo List 看透单向数据流与不可变数据
前端
软件开发技术深度爱好者24 分钟前
国际音标魔法实验室工具HTML5实现
前端·html5·英语学习
FogLetter25 分钟前
嘘!WebSocket正在“偷听”你的网络请求——全双工通信的魔法
前端·面试
用户9385156350726 分钟前
从零构建 React Todo 应用:组件化设计与数据流深度解析
前端·javascript
IT_陈寒29 分钟前
Python线程池把我坑惨了,这些盲区你不踩?
前端·人工智能·后端
张人玉40 分钟前
基于 Vue 3 + ECharts + Express + SQLite 的可视化大屏与业务管理系统——YOLOv8 高精度车辆行人检测与计数系统
数据库·vue.js·yolo·sqlite·echarts·express
程序员爱钓鱼1 小时前
第一个 Go 程序:Hello World
前端·后端·go
阳光是sunny1 小时前
LangGraph 核心概念详解:从编译到可视化
前端·人工智能·后端
不好听6132 小时前
React 父子组件通信:从 Todo 应用理解单向数据流
前端·react.js
_lucas2 小时前
做了一个glsl在线调试工具
前端·javascript·three.js