Vue-从内置指令到自定义指令实战
前言
在 Vue 的开发世界里,“指令(Directives)”是连接模板与底层 DOM 的桥梁。除了官方提供的强大内置指令外,Vue 还允许我们根据业务需求自定义指令。本文将带你一次性梳理 Vue 指令体系,并手把手实现一个高频实用的“一键复制”指令。
一、 Vue 内置指令全家桶
在深入自定义指令之前,我们先复习一下这些每天都在用的“老朋友”。内置指令以 v- 开头,是 Vue 预设的特殊属性。
| 指令 | 作用描述 | 核心要点 | |
|---|---|---|---|
v-bind |
响应式地更新 HTML 属性 | 简写为 :,如 :src、:class
|
|
v-on |
绑定事件监听器 | 简写为 @,如 @click
|
|
v-model |
在表单及组件上创建双向绑定 | 它是 v-bind 与 v-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 对应 | 执行时机 |
|---|---|---|
beforeMount |
bind |
指令第一次绑定到元素时调用 |
mounted |
inserted |
绑定元素插入父节点时调用 |
beforeUpdate |
update |
元素所在组件 VNode 更新前 |
updated |
componentUpdated |
组件及子组件全部更新后调用 |
unmounted |
unbind |
指令与元素解绑且元素已卸载 |
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 }
-
-
vnode:Vue编译生成的虚拟节点 -
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>
四、 总结
-
内置指令覆盖了 90% 的开发场景,应熟练掌握其简写与区别(如
v-ifvsv-show)。 -
自定义指令是操作 DOM 的最后防线,通过
mounted和updated钩子可以实现极其灵活的逻辑。 -
注意规范:在 Vue 3 + TS 环境下,务必为指令和参数标记类型,以确保代码的健壮性。