首页 / Vue3 入门教程 / 自定义指令

Vue3 入门教程

自定义指令

本教程共 40 篇 · 第 30 篇 · 更新于 2026-07-29 · 约 6 分钟阅读

VueVue3 入门教程Custom Directives自定义指令v-focus

30. 自定义指令

本节目标:搞清楚什么时候该用自定义指令,怎么用,以及它的生命周期钩子怎么运作。

自定义指令是什么

Vue 内置了 v-modelv-showv-if 等指令。当你需要直接操作 DOM 元素来完成某个功能时,自定义指令就派上用场了。

它跟组件、组合式函数并列为 Vue 的三种代码复用方式:

方式用途
组件复用 UI + 逻辑
组合式函数复用有状态逻辑
自定义指令复用 DOM 底层操作

一个例子:自动聚焦

假设你希望页面加载后 input 自动聚焦。HTML5 有 autofocus 属性,但它只在初次加载时生效。如果元素是 Vue 动态插入的,autofocus 就不管用了。

自定义指令来解决:

<script setup>
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>
Tip

<script setup> 里,以 v 开头的驼峰变量会自动识别为自定义指令。vFocus 在模板里写成 v-focus

生命周期钩子

自定义指令有一组跟组件类似的钩子,在元素的不同阶段触发:

const myDirective = {
  // 元素绑定到组件、但 DOM 还没插入时
  created(el, binding, vnode) {},
  // 插入 DOM 之前
  beforeMount(el, binding, vnode) {},
  // 父组件及子元素都插入到 DOM 后
  mounted(el, binding, vnode) {},
  // 父组件更新之前
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 父组件及子元素都更新后
  updated(el, binding, vnode, prevVnode) {},
  // 父组件卸载之前
  beforeUnmount(el, binding, vnode) {},
  // 父组件卸载时
  unmounted(el, binding, vnode) {}
}
Note

这些钩子都是可选的。实际开发中大部分场景只用到 mountedupdated

钩子参数

每个钩子都能拿到这些参数:

参数说明
el指令绑定的 DOM 元素,直接操作
binding包含指令相关信息的对象
vnode元素的虚拟 DOM 节点
prevVnode上一次渲染的虚拟节点(仅 beforeUpdateupdated

binding 对象里有这些属性:

属性说明例子
value指令接收的值v-demo="1+1"2
oldValue上一次的值beforeUpdate / updated
arg指令参数v-demo:foo"foo"
modifiers修饰符对象v-demo.foo.bar{ foo: true, bar: true }
instance使用该指令的组件实例
dir指令的定义对象本身

举例说明,如果你这样写模板:

<div v-example:foo.bar="baz"></div>

那么 binding 对象就是:

{
  arg: 'foo',
  modifiers: { bar: true },
  value: /* baz 的值 */
}
Note

除了 el,其他参数都应该是只读的。别去改它们,要用 el.dataset 来跨钩子共享信息。

动态参数

指令参数也可以是响应式的:

<div v-example:[arg]="value"></div>

当你改变 arg 的值时,Vue 会重新执行指令的更新逻辑。

函数简写

很多指令在 mountedupdated 时做的事情完全一样,其他钩子用不到。这时候可以写成函数形式:

app.directive('color', (el, binding) => {
  // mounted 和 updated 都会调用这段逻辑
  el.style.color = binding.value
})

模板里使用:

<div v-color="color"></div>

对象字面量传值

如果想传多个值,可以直接传一个对象:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // 'white'
  console.log(binding.value.text) // 'hello!'
})

局部注册 vs 全局注册

前面 <script setup> 里直接定义变量是局部注册——只能当前组件用。如果想全局生效,在 main.js 里注册:

import { createApp } from 'vue'

const app = createApp({})

app.directive('highlight', {
  mounted(el) {
    el.style.backgroundColor = 'yellow'
  }
})

在组件上用自定义指令

自定义指令可以用于组件,但只会作用于组件的根元素

Warning

不推荐在组件上使用自定义指令。如果组件有多个根节点,指令会被忽略并抛出警告。

什么时候该用

自定义指令是”最后手段”。能用 v-bind 等内置指令解决就别自己造轮子。适合的场景包括:

  • 自动聚焦输入框
  • 元素滚动到可视区域
  • 元素外部点击检测
  • 权限控制(没有权限就隐藏)

这些场景的共同点是:必须直接操作 DOM,没有别的声明式替代方案。

本节回顾

  • 自定义指令用于复用低级别 DOM 操作
  • 有 7 个生命周期钩子,mounted 最常用
  • binding 对象包含 valueargmodifiers
  • 函数简写适合 mountedupdated 逻辑一致的场景
  • 全局用 app.directive(),局部在 <script setup> 里用 v 前缀变量
  • 优先考虑内置指令,自定义指令是兜底方案