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

相关推荐
deli00712 分钟前
1 个汉字算几个 token?实测中英文 prompt 与代码的编码差异
前端
无限聚核18 分钟前
抖音 certificate.prepare 之后怎么验券?和美团 msconsume 差在哪,无限聚核
前端·微信小程序
2603_9658966228 分钟前
Grid 网格布局|CSS 二维页面排版体系
前端·css
有味道的男人43 分钟前
搜好货平台关键词商品搜索接口:分页容错与商品数据轻量化提取方案
服务器·前端·javascript
lovingsoft1 小时前
Chrome Lighthouse 详解
前端·chrome
Qlly2 小时前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝2 小时前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦2 小时前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡2 小时前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI2 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战