模式切换
Vue 全局指令与局部指令详解
指令(Directives)是 Vue 中用于直接操作 DOM 的特殊属性,分为全局注册和局部注册两种方式,各有其适用场景和实现方法。
全局指令
定义与注册
在应用级别注册,对所有组件可用:
javascript
// main.js
import { createApp } from 'vue'
const app = createApp(App)
// 注册全局指令
app.directive('focus', {
mounted(el) {
el.focus()
}
})
app.mount('#app')
核心特点
特性 | 说明 |
---|---|
可用范围 | 整个应用的所有组件 |
生命周期 | 与应用生命周期一致 |
适用场景 | 高频复用指令(如权限控制、工具类指令) |
命名冲突 | 后注册的会覆盖先注册的同名指令 |
典型用例
javascript
// 自动聚焦指令
app.directive('focus', {
mounted(el) {
el.focus()
},
updated(el) {
if (el.dataset.autofocus === 'true') {
el.focus()
}
}
})
// 权限控制指令
app.directive('permission', {
beforeMount(el, binding) {
if (!checkPermission(binding.value)) {
el.style.display = 'none'
}
}
})
局部指令
定义与注册
在组件选项中定义,仅当前组件可用:
选项式 API
javascript
export default {
directives: {
highlight: {
mounted(el, binding) {
el.style.backgroundColor = binding.value || 'yellow'
}
}
}
}
组合式 API
javascript
<script setup>
const vHighlight = {
mounted(el, binding) {
el.style.backgroundColor = binding.value || 'yellow'
}
}
</script>
核心特点
特性 | 说明 |
---|---|
可用范围 | 仅当前组件及其子组件 |
生命周期 | 随组件销毁而移除 |
适用场景 | 组件专用指令(如表单验证、UI特效) |
命名冲突 | 只影响当前组件 |
典型用例
javascript
// 输入框字数统计
const vCount = {
mounted(el, binding) {
const span = document.createElement('span')
el.parentNode.insertBefore(span, el.nextSibling)
el.addEventListener('input', () => {
span.textContent = `${el.value.length}/${binding.value}`
})
}
}
// 拖拽指令
const vDrag = {
mounted(el) {
el.style.cursor = 'move'
el.onmousedown = (e) => {
// 拖拽逻辑实现
}
}
}
指令生命周期钩子
两种指令类型共享相同的生命周期:
钩子 | 调用时机 | 典型用途 |
---|---|---|
created | 元素属性初始化后 | 初始化非响应式设置 |
beforeMount | 元素插入DOM前 | 获取初始DOM状态 |
mounted | 元素插入DOM后 | DOM操作、事件监听 |
beforeUpdate | 组件更新前 | 获取更新前状态 |
updated | 组件更新后 | 基于新状态的DOM操作 |
beforeUnmount | 组件卸载前 | 清理事件监听器 |
unmounted | 组件卸载后 | 最终清理 |
javascript
app.directive('example', {
created(el, binding, vnode) {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {}, // 等同于Vue 2的beforeDestroy
unmounted() {} // 等同于Vue 2的destroyed
})
指令参数详解
每个钩子函数接收以下参数:
javascript
{
mounted(el, binding, vnode, prevVnode) {
// el: 指令绑定的DOM元素
// binding: 包含指令信息的对象
// vnode: 当前虚拟节点
// prevVnode: 上一个虚拟节点(仅beforeUpdate/updated)
}
}
binding 对象结构
属性 | 说明 | 示例 (v-demo:foo.bar="baz" ) |
---|---|---|
value | 指令绑定的值 | baz |
oldValue | 前一个值(仅update) | - |
arg | 指令参数 | 'foo' |
modifiers | 修饰符对象 | { bar: true } |
instance | 组件实例 | 当前组件实例 |
dir | 指令定义对象 | 指令配置对象 |
最佳实践
选择注册方式
场景 | 推荐方式 |
---|---|
多个组件共享的工具指令 | 全局注册 |
特定组件的专用指令 | 局部注册 |
第三方库提供的指令 | 全局注册(通常由库处理) |
性能优化
避免频繁DOM操作:在
updated
中检查值是否真的变化javascriptupdated(el, binding) { if (binding.value !== binding.oldValue) { // 执行DOM操作 } }
事件监听清理:
javascriptmounted(el, binding) { const handler = () => { /*...*/ } el._clickHandler = handler el.addEventListener('click', handler) }, unmounted(el) { el.removeEventListener('click', el._clickHandler) }
指令组合
将多个功能拆分为独立指令,通过修饰符控制行为:
vue
<button
v-tooltip="'提示内容'"
v-tooltip.right
v-tooltip.delay="300"
>按钮</button>
与组件对比
特性 | 指令 | 组件 |
---|---|---|
主要用途 | 底层DOM操作 | 构建UI模块 |
模板能力 | 无 | 完整模板支持 |
生命周期 | 精细DOM钩子 | 组件生命周期 |
复用方式 | 功能复用 | UI+逻辑复用 |
何时用指令?
- 需要直接操作DOM(如聚焦、拖拽)
- 添加通用行为(如权限控制)
- 集成第三方DOM库
经典指令实现
图片懒加载
javascript
app.directive('lazy', {
mounted(el, binding) {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
observer.observe(el)
}
})
使用:
html
<img v-lazy="'https://example.com/image.jpg'">
点击外部关闭
javascript
const vClickOutside = {
mounted(el, binding) {
el._clickOutside = (event) => {
if (!el.contains(event.target)) {
binding.value(event)
}
}
document.addEventListener('click', el._clickOutside)
},
unmounted(el) {
document.removeEventListener('click', el._clickOutside)
}
}
使用:
html
<div v-click-outside="closeDropdown">内容</div>