自定义指令
什么是自定义指令?
自定义指令是 Vue3 中的一个高级特性,允许我们扩展 HTML 元素的功能,通过自定义属性来操作 DOM。自定义指令可以用于添加事件监听器、修改元素样式、操作 DOM 等。
1. 指令的生命周期钩子
自定义指令有以下生命周期钩子:
created:指令绑定到元素时调用beforeMount:元素挂载到 DOM 前调用mounted:元素挂载到 DOM 后调用beforeUpdate:元素更新前调用updated:元素更新后调用beforeUnmount:元素卸载前调用unmounted:元素卸载后调用
2. 全局自定义指令
全局自定义指令可以在整个应用中使用,通过 app.directive() 方法注册。
示例:创建全局自定义指令
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 注册全局自定义指令
app.directive('focus', {
// 元素挂载到 DOM 后自动聚焦
mounted(el) {
el.focus()
}
})
// 注册带生命周期的指令
app.directive('highlight', {
created(el, binding) {
console.log('指令创建')
},
mounted(el, binding) {
// 绑定值可以是字符串、对象或表达式
el.style.backgroundColor = binding.value || 'yellow'
el.style.color = binding.arg || 'black'
// 修饰符
if (binding.modifiers.bold) {
el.style.fontWeight = 'bold'
}
if (binding.modifiers.italic) {
el.style.fontStyle = 'italic'
}
},
updated(el, binding) {
// 当绑定值变化时更新
el.style.backgroundColor = binding.value || 'yellow'
}
})
app.mount('#app')
使用全局自定义指令
<template>
<div>
<!-- 基本用法 -->
<input v-focus placeholder="自动聚焦" />
<!-- 带绑定值 -->
<p v-highlight="'lightblue'">蓝色背景</p>
<!-- 带参数和修饰符 -->
<p v-highlight:red.bold.italic="'pink'">红色文字,粉色背景,粗体斜体</p>
<!-- 动态绑定值 -->
<p v-highlight="dynamicColor">动态背景色</p>
<button @click="dynamicColor = 'green'">变为绿色</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const dynamicColor = ref('yellow');
</script>
3. 局部自定义指令
局部自定义指令只能在当前组件中使用,通过 directives 选项注册。
示例:创建局部自定义指令
<template>
<div>
<input v-focus placeholder="自动聚焦" />
<p v-tooltip="'这是一个提示'">鼠标悬停查看提示</p>
</div>
</template>
<script setup>
// 局部自定义指令
const vFocus = {
mounted(el) {
el.focus()
}
}
const vTooltip = {
mounted(el, binding) {
el.addEventListener('mouseenter', () => {
// 创建提示元素
const tooltip = document.createElement('div')
tooltip.className = 'tooltip'
tooltip.textContent = binding.value
tooltip.style.position = 'absolute'
tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'
tooltip.style.color = 'white'
tooltip.style.padding = '5px 10px'
tooltip.style.borderRadius = '4px'
tooltip.style.zIndex = '1000'
// 计算位置
const rect = el.getBoundingClientRect()
tooltip.style.left = `${rect.left + window.scrollX}px`
tooltip.style.top = `${rect.top + window.scrollY - 30}px`
// 添加到文档
document.body.appendChild(tooltip)
// 保存到元素上,以便后续移除
el._tooltip = tooltip
})
el.addEventListener('mouseleave', () => {
// 移除提示元素
if (el._tooltip) {
document.body.removeChild(el._tooltip)
el._tooltip = null
}
})
},
unmounted(el) {
// 清理
if (el._tooltip) {
document.body.removeChild(el._tooltip)
}
}
}
// 导出指令
defineExpose({
vFocus,
vTooltip
})
</script>
4. 函数式自定义指令
对于简单的指令,可以使用函数式写法,它会在 mounted 和 updated 钩子中执行。
示例:函数式自定义指令
// 全局函数式指令
app.directive('color', (el, binding) => {
el.style.color = binding.value
})
// 局部函数式指令
const vColor = (el, binding) => {
el.style.color = binding.value
}
5. 指令的参数和修饰符
自定义指令可以接收参数和修饰符,通过 binding 对象访问。
示例:使用参数和修饰符
<template>
<div>
<!-- 参数:arg -->
<p v-position:top="'100px'">顶部定位</p>
<!-- 修饰符:modifiers -->
<p v-position:bottom.fixed="'50px'">底部固定定位</p>
</div>
</template>
<script setup>
const vPosition = {
mounted(el, binding) {
const position = binding.modifiers.fixed ? 'fixed' : 'absolute'
el.style.position = position
if (binding.arg === 'top') {
el.style.top = binding.value
} else if (binding.arg === 'bottom') {
el.style.bottom = binding.value
} else if (binding.arg === 'left') {
el.style.left = binding.value
} else if (binding.arg === 'right') {
el.style.right = binding.value
}
}
}
</script>
6. 自定义指令的应用场景
- 自动聚焦输入框
- 添加滚动监听
- 实现拖拽功能
- 添加水印效果
- 实现懒加载
- 添加权限控制
- 实现自定义表单验证
7. 自定义指令的最佳实践
- 只在必要时使用自定义指令,优先考虑组件
- 保持指令的职责单一
- 使用清晰的命名,以 v- 前缀开头
- 在 unmounted 钩子中清理资源,如事件监听器、定时器等
- 对于复杂逻辑,考虑使用组合式函数封装
8. 自定义指令 vs 组件
| 特性 | 自定义指令 | 组件 |
|---|---|---|
| 用途 | 扩展 DOM 元素功能 | 构建 UI 组件 |
| 封装程度 | 轻量级,专注于 DOM 操作 | 完整的组件封装,包含模板、逻辑和样式 |
| 复用性 | 可以全局或局部复用 | 可以全局或局部复用 |
| 适用场景 | DOM 操作、事件监听、样式修改 | UI 组件、复杂交互 |