Vue-从内置指令到自定义指令实战

119 阅读3分钟

前言

在 Vue 的开发世界里,“指令(Directives)”是连接模板与底层 DOM 的桥梁。除了官方提供的强大内置指令外,Vue 还允许我们根据业务需求自定义指令。本文将带你一次性梳理 Vue 指令体系,并手把手实现一个高频实用的“一键复制”指令。

一、 Vue 内置指令全家桶

在深入自定义指令之前,我们先复习一下这些每天都在用的“老朋友”。内置指令以 v- 开头,是 Vue 预设的特殊属性。

指令作用描述核心要点
v-bind响应式地更新 HTML 属性简写为 :,如 :src:class
v-on绑定事件监听器简写为 @,如 @click
v-model在表单及组件上创建双向绑定它是 v-bindv-on 的语法糖
v-if / v-else根据条件渲染/销毁元素真正的条件渲染(销毁与重建)
v-show根据条件切换元素的显示基于 CSS 的 display: none 切换
v-for基于源数据多次渲染元素建议必须绑定唯一的 :key
v-html更新元素的 innerHTML注意:易导致 XSS 攻击,慎用
v-once只渲染元素和组件一次随后的重新渲染将跳过该部分,用于优化性能

二、 自定义指令:像 v-model 一样强大

1. 核心概念

自定义指令主要用于提高代码复用性。当你发现自己在多个组件中都在操作同一个 DOM 逻辑时,就该考虑将其封装为指令了。

2. 生命周期(钩子函数)

Vue 3 重构了指令钩子,使其与组件生命周期完美对齐:

Vue 3 钩子Vue 2 对应执行时机
beforeMountbind指令第一次绑定到元素时调用
mountedinserted绑定元素插入父节点时调用
beforeUpdateupdate元素所在组件 VNode 更新前
updatedcomponentUpdated组件及子组件全部更新后调用
unmountedunbind指令与元素解绑且元素已卸载

3. 钩子函数参数

指令对象的钩子函数中都带有如下参数:

  • el: 绑定的真实 DOM。

  • binding: 对象,包含

    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 ``update/beforeUpdate 和 componentUpdated/updated` 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnodeVue 编译生成的虚拟节点

  • oldVnode:上一个虚拟节点,仅在 update/beforeUpdate 和 componentUpdated/updated 钩子中可用


三、 实战:实现“一键复制”指令 v-copy

1. 指令逻辑实现 (/libs/directives/copy.ts)

import { Directive, DirectiveBinding } from 'vue';

export const copyDirective: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    el.style.cursor = 'copy';
    
    // 绑定点击事件
    el.addEventListener('click', () => {
      const textToCopy = binding.value;
      
      if (!textToCopy) {
        console.warn('v-copy: 无复制内容');
        return;
      }

      // 现代浏览器 API
      if (navigator.clipboard && window.isSecureContext) {
        navigator.clipboard.writeText(String(textToCopy))
          .then(() => alert('复制成功!'))
          .catch(() => alert('复制失败'));
      } else {
        // 兼容降级方案
        const textarea = document.createElement('textarea');
        textarea.value = String(textToCopy);
        textarea.style.position = 'fixed';
        textarea.style.left = '-9999px';
        document.body.appendChild(textarea);
        textarea.select();
        try {
          document.execCommand('copy');
          alert('复制成功!');
        } catch (err) {
          console.error('复制失败', err);
        }
        document.body.removeChild(textarea);
      }
    });
  }
};

2. 全局注册与使用

注册 (main.ts):

import { createApp } from 'vue';
import App from './App.vue';
import { copyDirective } from './libs/directives/copy';

const app = createApp(App);
app.directive('copy', copyDirective); // 全局注册
app.mount('#app');

使用:

<template>
  <button v-copy="'这是要复制的内容'">点击复制</button>
</template>

四、 总结

  1. 内置指令覆盖了 90% 的开发场景,应熟练掌握其简写与区别(如 v-if vs v-show)。

  2. 自定义指令是操作 DOM 的最后防线,通过 mountedupdated 钩子可以实现极其灵活的逻辑。

  3. 注意规范:在 Vue 3 + TS 环境下,务必为指令和参数标记类型,以确保代码的健壮性。