vue自定义指令

Vue 自定义指令(Custom Directives)是一种扩展 Vue 功能的方式,允许你直接操作 DOM 元素,用于处理一些常见的 DOM 操作逻辑(如聚焦、权限控制、动画等)。

自定义指令的基本结构

自定义指令可以注册为全局指令或局部指令,包含一系列钩子函数(类似组件生命周期)。

1. 全局注册
javascript 复制代码
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})
2. 局部注册
javascript 复制代码
export default {
  directives: {
    focus: {
      // 指令的定义
      inserted: function (el) {
        el.focus()
      }
    }
  }
}
使用方式
vue 复制代码
<template>
  <input v-focus />
</template>

指令的钩子函数

指令有 5 个钩子函数(Vue 2):

钩子函数 说明
bind 只调用一次,指令第一次绑定到元素时调用
inserted 被绑定元素插入父节点时调用
update 所在组件的 VNode 更新时调用(可能发生在子 VNode 更新前)
componentUpdated 所在组件的 VNode 及其子 VNode 全部更新后调用
unbind 只调用一次,指令与元素解绑时调用

钩子函数参数

  • el:指令所绑定的元素,可以直接操作 DOM
  • binding:一个对象,包含指令相关信息(如 namevalueexpression 等)
  • vnode:Vue 编译生成的虚拟节点
  • oldVnode:上一个虚拟节点(仅在 updatecomponentUpdated 钩子中可用)

实用示例

1. 权限控制指令(v-permission)
javascript 复制代码
// 全局注册 v-permission
Vue.directive('permission', {
  inserted(el, binding) {
    // 获取用户权限列表(实际项目中可能从 Vuex 或接口获取)
    const userPermissions = ['edit', 'delete']
    // 获取指令绑定的值(如 v-permission="['delete']")
    const requiredPermissions = binding.value
    
    // 检查是否有权限,无权限则移除元素
    const hasPermission = requiredPermissions.some(perm => 
      userPermissions.includes(perm)
    )
    
    if (!hasPermission) {
      el.parentNode && el.parentNode.removeChild(el)
    }
  }
})
vue 复制代码
<!-- 只有拥有 delete 权限的用户才会看到该按钮 -->
<button v-permission="['delete']">删除</button>
2. 防抖指令(v-debounce)
javascript 复制代码
Vue.directive('debounce', {
  bind(el, binding) {
    const { value, arg = 500 } = binding // arg 为防抖时间,默认 500ms
    let timer = null
    
    el.addEventListener('click', () => {
      if (timer) clearTimeout(timer)
      timer = setTimeout(() => {
        value() // 执行绑定的方法
      }, arg)
    })
  }
})
vue 复制代码
<!-- 点击后 1000ms 才执行 handleClick 方法 -->
<button v-debounce:[1000]="handleClick">提交</button>
3. Vue 3 中的自定义指令

Vue 3 中指令钩子函数有所调整,更贴近组合式 API:

javascript 复制代码
// Vue 3 全局注册
import { createApp } from 'vue'
const app = createApp(App)

app.directive('focus', {
  // 在元素挂载后执行(替代 Vue 2 的 inserted)
  mounted(el) {
    el.focus()
  }
})

Vue 3 指令钩子:createdbeforeMountmountedbeforeUpdateupdatedbeforeUnmountunmounted

动态指令参数

指令可以接收动态参数,使指令更灵活:

vue 复制代码
<!-- 动态设置背景色 -->
<div v-bg:[color]="'red'"></div>

<script>
Vue.directive('bg', {
  bind(el, binding) {
    // binding.arg 为动态参数(color 的值)
    // binding.value 为指令值('red')
    el.style[binding.arg] = binding.value
  }
})

export default {
  data() {
    return { color: 'backgroundColor' }
  }
}
</script>

注意事项

  1. 自定义指令主要用于底层 DOM 操作,若能通过组件或 props 实现的功能,优先使用组件
  2. 避免在指令中过度处理复杂逻辑,保持指令单一职责
  3. Vue 3 中指令的钩子函数与组件生命周期更一致,使用时注意区分

通过自定义指令,可以封装通用的 DOM 操作逻辑,提高代码复用性。

vue3的自定义指令钩子函数

在 Vue 3 中,自定义指令的钩子函数进行了调整,命名更贴近组件的生命周期,使其更易于理解和使用。以下是 Vue 3 中自定义指令的钩子函数及详细说明:

Vue 3 自定义指令的钩子函数

Vue 3 提供了 7 个指令钩子函数,按执行顺序排列如下:

钩子函数 触发时机
created 指令绑定到元素上且组件的属性或事件监听器被应用之前调用(很少使用)
beforeMount 指令绑定的元素被挂载到 DOM 之前调用
mounted 指令绑定的元素被挂载到 DOM 后调用(最常用,替代 Vue 2 的 inserted
beforeUpdate 指令所在组件的 VNode 更新前调用(可能发生在子 VNode 更新前)
updated 指令所在组件的 VNode 及其子 VNode 全部更新后调用
beforeUnmount 指令绑定的元素从 DOM 卸载前调用
unmounted 指令绑定的元素从 DOM 卸载后调用(替代 Vue 2 的 unbind

钩子函数的参数

每个钩子函数都接收以下 4 个参数(与 Vue 2 类似,但部分属性有调整):

  • el:指令所绑定的 DOM 元素,可直接操作 DOM

复制代码
  binding

:包含指令信息的对象,结构如下:

  • value:指令的绑定值(如 v-directive="123" 中的 123

  • oldValue:指令绑定的前一个值(仅在 beforeUpdateupdated 中可用)

  • arg:指令的参数(如 v-directive:foo 中的 foo

  • modifiers:指令的修饰符对象(如 v-directive.foo.bar{ foo: true, bar: true }

  • instance:使用该指令的组件实例

  • dir:指令的定义对象(包含钩子函数等)

  • vnode:Vue 编译生成的虚拟节点

  • prevVnode:上一个虚拟节点(仅在 beforeUpdateupdated 中可用)

示例:实现一个 Vue 3 自定义指令

以下是一个完整示例

vue 复制代码
<template>
  <!-- 使用自定义指令 -->
  <button v-copy="copyText" v-copy:success="onCopySuccess">
    点击复制
  </button>
</template>

<script setup>
import { ref } from 'vue'
import { copyDirective } from './directives'

// 注册局部指令
const directives = {
  copy: copyDirective
}

// 要复制的文本
const copyText = ref('这是要复制的内容')

// 复制成功的回调
const onCopySuccess = (text) => {
  alert(`复制成功:${text}`)
}
</script>

<!-- directives.js -->
<script>
export const copyDirective = {
  // 元素挂载后
  mounted(el, binding) {
    // 存储要复制的文本和回调函数
    el.copyText = binding.value
    el.successCallback = binding.arg === 'success' ? binding.value : null

    // 点击事件处理函数
    el.handleClick = () => {
      // 复制文本到剪贴板
      navigator.clipboard.writeText(el.copyText)
        .then(() => {
          // 触发成功回调
          if (el.successCallback && typeof el.successCallback === 'function') {
            el.successCallback(el.copyText)
          }
        })
        .catch(err => {
          console.error('复制失败:', err)
        })
    }

    // 绑定点击事件
    el.addEventListener('click', el.handleClick)
  },

  // 组件更新时(值可能变化)
  updated(el, binding) {
    // 更新存储的文本和回调
    el.copyText = binding.value
    if (binding.arg === 'success') {
      el.successCallback = binding.value
    }
  },

  // 元素卸载前
  beforeUnmount(el) {
    // 移除事件监听,避免内存泄漏
    el.removeEventListener('click', el.handleClick)
  }
}
</script>

关键变化与注意事项

  1. 钩子命名更直观

    mounted 替代 Vue 2 的 inserted(元素挂载到 DOM 后触发),用 unmounted 替代 unbind(元素卸载后触发),更符合组件生命周期的命名习惯。

  2. 支持组合式 API

    <script setup> 中,局部指令通过 directives 对象注册,全局指令通过 app.directive() 注册(如 app.directive('copy', copyDirective))。

  3. 参数增强

    binding.instance 可以直接访问使用指令的组件实例,方便获取组件内的数据或方法。

  4. 清理逻辑

    需在 beforeUnmountunmounted 中移除事件监听、定时器等,避免内存泄漏(如示例中移除点击事件)。

常见使用场景

  • 操作 DOM(如自动聚焦 v-focus、滚动加载 v-infinite-scroll
  • 权限控制(如 v-permission 决定元素是否显示)
  • 交互增强(如 v-longpress 长按指令、v-debounce 防抖指令)

Vue 3 的自定义指令钩子设计更符合直觉,同时保留了灵活操作 DOM 的能力,是扩展 Vue 功能的重要方式。

相关推荐
辻戋2 小时前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保2 小时前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun3 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp3 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.3 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl6 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫7 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友7 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理9 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻9 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js