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 功能的重要方式。

相关推荐
niusir2 小时前
Zustand 实战:10 行代码搞定全局状态
前端·javascript·react.js
niusir2 小时前
React 状态管理的演进与最佳实践
前端·javascript·react.js
张愚歌2 小时前
快速上手Leaflet:轻松创建你的第一个交互地图
前端
唐某人丶2 小时前
教你如何用 JS 实现 Agent 系统(3)—— 借鉴 Cursor 的设计模式实现深度搜索
前端·人工智能·aigc
看到我请叫我铁锤2 小时前
vue3使用leaflet的时候高亮显示省市区
前端·javascript·vue.js
南囝coding2 小时前
Vercel 发布 AI Gateway 神器!可一键访问数百个模型,助力零门槛开发 AI 应用
前端·后端
AI大模型2 小时前
前端学 AI 不用愁!手把手教你用 LangGraph 实现 ReAct 智能体(附完整流程 + 代码)
前端·llm·agent
小红3 小时前
网络通信基石:从IP地址到子网划分的完整指南
前端·网络协议
一枚前端小能手3 小时前
🔥 前端储存这点事 - 5个存储方案让你的数据管理更优雅
前端·javascript