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

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A3 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
honkun63 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19913 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端