一、指令初探:什么是自定义指令?

大家好,我是老张。今天咱们开始聊 Vue 自定义指令。

说实话,我刚接触 Vue 那会儿,对自定义指令这玩意儿挺不屑的。心想:内置指令都够用了,干嘛还要自己造轮子?直到我在一个真实项目中栽了跟头——有个需求是要给所有输入框加一个自动聚焦功能,还要在失焦时做数据校验。用内置指令?根本搞不定。嗯,从那以后,我才真正开始研究自定义指令。

1.1 先回顾一下 Vue 内置指令

在聊自定义指令之前,咱们得先搞清楚 Vue 到底给了我们哪些现成的工具。说白了,内置指令就是 Vue 官方帮我们封装好的 DOM 操作能力。

指令 作用 我常用的场景
v-if 条件渲染(销毁/重建) 权限控制、弹窗显示
v-show 条件渲染(切换 display) 频繁切换的 UI 元素
v-for 列表渲染 表格、卡片列表
v-bind 动态绑定属性 绑定 class、style、props
v-on 事件监听 点击、输入、滚动
v-model 双向数据绑定 表单控件
v-slot 插槽 组件内容分发
v-pre 跳过编译 显示原始 Mustache 标签
v-cloak 防止闪烁 配合 CSS 使用
v-once 只渲染一次 静态内容优化
我的小建议:v-if 和 v-show 的区别,面试常考。但实际项目中,我一般遵循一个原则:如果切换频率高,用 v-show;如果切换频率低或者有复杂子组件,用 v-if。别问为什么,问就是性能踩过坑。

1.2 什么是自定义指令?

自定义指令,说白了就是让你能直接操作 DOM 的一种方式。Vue 的核心是数据驱动视图,但有些时候,你确实需要直接跟 DOM 打交道——比如聚焦、拖拽、权限控制。

你想想看,Vue 的内置指令已经覆盖了 80% 的常见场景。但剩下的 20%,比如「点击外部区域关闭弹窗」、「长按触发事件」、「图片加载失败显示占位图」——这些内置指令就无能为力了。这时候,自定义指令就派上用场了。

核心概念:自定义指令本质上是一个对象,它包含一组生命周期钩子函数。这些钩子会在 DOM 元素的不同阶段被调用。你可以理解为:Vue 给了你一个「钩子」,让你在元素创建、更新、销毁时执行自己的逻辑。

1.3 自定义指令的应用场景

我在项目中总结了几类最常见的场景,分享给你:

  1. DOM 操作类:自动聚焦、拖拽、缩放、滚动加载
  2. 事件处理类:防抖、节流、点击外部、长按
  3. 权限控制类:按钮级权限、元素显隐
  4. UI 增强类:水印、复制、图片懒加载、文字溢出省略
  5. 第三方集成类:封装 jQuery 插件、图表初始化

举个例子,我曾经在一个后台管理系统中,需要实现「按钮级权限控制」。如果用 v-if 配合计算属性,每个按钮都要写一遍判断逻辑,代码冗余到爆炸。后来我封装了一个 v-permission 指令,一行代码搞定:

<button v-permission="'user:delete'">删除用户</button>

你看,是不是清爽多了?

1.4 什么时候不该用自定义指令?

注意:自定义指令不是万能的。我个人建议,能用组件解决的问题,尽量别用指令。比如:
  • 复杂的 UI 交互 → 用组件
  • 需要响应式数据 → 用计算属性或 watch
  • 跨组件通信 → 用 provide/inject 或 Vuex

我曾经见过有人用自定义指令做表单校验,结果代码维护起来比老太太的裹脚布还长。嗯,后来全重构了。

1.5 一个简单的例子:v-focus

咱们先看一个最简单的自定义指令——自动聚焦。虽然 Vue 3 里可以用 refonMounted 实现,但用指令更优雅:

// 注册一个全局自定义指令
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

// 在模板中使用
<input v-focus placeholder="自动聚焦" />

就这么几行代码,所有输入框都能自动聚焦了。你想想看,如果不用指令,你得在每个组件里写 refonMountednextTick……重复劳动不说,还容易出错。

避坑指南:我曾经在动态渲染的列表里用 v-focus,发现聚焦不生效。后来排查发现,是因为元素还没挂载到 DOM 上就调用了 focus()。解决方案很简单:在 mounted 钩子里加个 nextTick,或者用 updated 钩子。细节决定成败啊。

1.6 自定义指令 vs 组件:怎么选?

很多新手会纠结这个问题。我个人的判断标准很简单:

  • 如果这个功能需要「包裹」子内容,或者有复杂的模板结构 → 用组件
  • 如果这个功能只是对单个 DOM 元素做「增强」→ 用指令

举个例子:一个「拖拽弹窗」功能。弹窗本身有标题、内容、按钮,这是组件的事。但「拖拽」这个行为,只是对弹窗的根元素做事件监听和位置计算,用指令更合适。

好了,这一章咱们先热个身。下一章我会带你深入自定义指令的「钩子函数」和「参数传递」,到时候咱们手写一个完整的权限指令。敬请期待。