Vue 2 与 Vue 3 自定义指令(Directive)详解

Vue 2 与 Vue 3 自定义指令(Directive)详解

Vue 的自定义指令(Directive)允许开发者直接操作 DOM,实现原生 HTML 无法直接实现的功能(如自动聚焦、滚动加载等)。以下是 Vue 2 和 Vue 3 中自定义指令的核心差异及详细用法。


一、基本用法对比

1. Vue 2 中注册指令

在 Vue 2 中,指令的定义和使用是基于 Vue.directive() 进行全局注册的。它主要通过 钩子函数 来与 DOM 交互,例如 bindinsertedupdatecomponentUpdatedunbind

  • 全局注册:通过 Vue.directive() 全局注册指令,所有组件可用。
  • 局部注册:通过组件的 directives 选项局部注册。
// 全局注册自定义指令
Vue.directive('my-directive', {
  // 指令绑定到元素时调用
  bind(el, binding, vnode) {
    console.log('指令绑定', el, binding, vnode);
  },
  // 指令插入到 DOM 中时调用
  inserted(el) {
    console.log('指令插入', el);
  },
  // 元素更新时调用
  update(el, binding) {
    console.log('元素更新', el, binding);
  },
  // 组件更新时调用
  componentUpdated(el) {
    console.log('组件更新', el);
  },
  // 指令解绑时调用
  unbind(el) {
    console.log('指令解绑', el);
  }
});

// 全局注册:v-focus
Vue.directive('focus', {
  inserted(el) {
    el.focus();
  }
});

// 局部注册
export default {
  directives: {
    focus: {
      inserted(el) {
        el.focus();
      }
    }
  }
};
2. Vue 3 中注册指令
  • 全局注册:通过应用实例的 directive() 方法注册,作用域限制在应用实例内。
  • 局部注册:与 Vue 2 类似,通过组件的 directives 选项。
// 全局注册
const app = createApp(App);
app.directive('focus', {
  mounted(el) {
    el.focus();
  }
});

// 局部注册
export default {
  directives: {
    focus: {
      mounted(el) {
        el.focus();
      }
    }
  }
};

二、指令生命周期钩子对比

Vue 2 指令钩子
钩子函数触发时机参数
bind指令第一次绑定到元素时el, binding, vnode
inserted元素插入父节点时el, binding, vnode
update组件更新时(可能发生在子组件更新前)el, binding, vnode, oldVnode
componentUpdated组件及子组件更新后el, binding, vnode, oldVnode
unbind指令与元素解绑时el, binding, vnode
Vue 3 指令钩子
钩子函数触发时机参数
beforeMount元素挂载到 DOM 前(类似 bindel, binding, vnode
mounted元素挂载到 DOM 后(类似 insertedel, binding, vnode
beforeUpdate组件更新前(类似 updateel, binding, vnode
updated组件更新后el, binding, vnode
beforeUnmount组件卸载前(类似 unbindel, binding, vnode
unmounted组件卸载后el, binding, vnode

三、核心差异详解

1. 钩子函数名称变化
  • Vue 2 的 bind → Vue 3 的 beforeMount
  • Vue 2 的 inserted → Vue 3 的 mounted
  • Vue 2 的 unbind → Vue 3 的 beforeUnmount
2. 参数对象差异
  • Vue 2 的 binding 对象

    {
      name: '指令名(不带 v-)',
      value: '指令绑定的值',
      oldValue: '旧值(仅在 update 和 componentUpdated 中可用)',
      expression: '绑定值的字符串形式(如 "user.name")',
      arg: '指令参数(如 v-my-directive:arg 中的 arg)',
      modifiers: '修饰符对象(如 v-my-directive.modifier 中的 { modifier: true })'
    }
    
  • Vue 3 的 binding 对象

    {
      instance: '当前组件实例',  // 新增属性
      value: '指令绑定的值',
      oldValue: '旧值(在 beforeUpdate 和 updated 中可用)',
      arg: '指令参数',
      modifiers: '修饰符对象',
      dir: '指令配置对象'  // 新增属性(包含所有钩子函数)
    }
    
    • 移除了 expressionrawName,新增 instancedir
3. 碎片(Fragment)支持
  • Vue 2:组件只能有一个根元素,指令绑定在根元素上。
  • Vue 3:组件支持多个根元素(Fragment),指令需明确绑定到具体元素。

四、实战示例

场景:实现一个文本高亮指令 v-highlight
Vue 2 实现
// 全局注册
Vue.directive('highlight', {
  bind(el, binding) {
    el.style.backgroundColor = binding.value || 'yellow';
  },
  update(el, binding) {
    el.style.backgroundColor = binding.value || 'yellow';
  }
});

// 使用
<template>
  <div v-highlight="'#ff0000'">高亮文本</div>
</template>
Vue 3 实现
// 全局注册
const app = createApp(App);
app.directive('highlight', {
  beforeMount(el, binding) {
    el.style.backgroundColor = binding.value || 'yellow';
  },
  updated(el, binding) {
    el.style.backgroundColor = binding.value || 'yellow';
  }
});

// 使用(支持多根元素)
<template>
  <div v-highlight="'#ff0000'">高亮文本</div>
  <p>其他内容</p>
</template>

五、高级用法

1. 动态指令参数
<!-- Vue 2 和 Vue 3 通用 -->
<template>
  <div v-mydir:[dynamicArg].modifier="value"></div>
</template>
v-mydir 是自定义指令。
[dynamicArg] 表示动态参数,指令的行为会根据这个动态参数的值而改变。
.modifier 是修饰符,用来改变指令的特定行为。
value 是绑定的值,用来传递指令的具体数据。
<template>
  <div>
    <!-- 动态设置背景颜色 -->
    <div v-color:[styleProp].animated="colorValue">
      动态背景色和动画
    </div>

    <!-- 动态设置文本颜色 -->
    <div v-color:[styleProp]="textColor">
      动态文本颜色
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      styleProp: 'backgroundColor',  // 动态参数,控制样式属性
      colorValue: 'lightblue',       // 要绑定的背景颜色值
      textColor: 'red'               // 要绑定的文本颜色值
    };
  },
  directives: {
    // 定义一个自定义指令 `v-color`
    color: {
      // 动态参数([styleProp])和修饰符(.animated)
      mounted(el, binding) {
        const styleProp = binding.arg; // 获取动态参数,控制样式属性
        const value = binding.value;    // 获取绑定的值
        const isAnimated = binding.modifiers.animated; // 获取是否启用动画的修饰符

        // 设置样式属性
        el.style[styleProp] = value;

        // 如果启用动画效果,添加过渡动画
        if (isAnimated) {
          el.style.transition = 'all 1s ease-in-out';
        }
      }
    }
  }
};
</script>

<style>
div {
  margin: 20px;
  padding: 20px;
  text-align: center;
}
</style>
2. 指令与 Composition API 结合(Vue 3)
import { ref, onMounted } from 'vue';

export default {
  setup() {
    const color = ref('red');
    return { color };
  }
};

// 自定义指令
app.directive('color', {
  mounted(el, binding) {
    el.style.color = binding.value;
  },
  updated(el, binding) {
    el.style.color = binding.value;
  }
});

六、迁移注意事项

  1. 钩子函数重命名:将 bind 改为 beforeMountinserted 改为 mountedunbind 改为 beforeUnmount
  2. 访问组件实例:Vue 3 中通过 binding.instance 访问,替代 Vue 2 的 vnode.context
  3. 碎片支持:确保指令绑定到具体的 DOM 元素,避免多根组件中的歧义。

七、总结对比表

特性Vue 2Vue 3
注册方式Vue.directive()(全局)app.directive()(应用实例作用域)
钩子函数bind, inserted, unbindbeforeMount, mounted, beforeUnmount
参数对象包含 expression新增 instancedir
碎片支持不支持(单根组件)支持(多根组件)
TypeScript 支持强(完整类型定义)
生命周期逻辑拆分较简单更细粒度(新增 updatedbeforeUpdate
类型支持需手动声明类型天然支持 TypeScript

通过对比可以看出,Vue 3 的指令系统更加模块化和灵活,同时解决了全局污染问题。如需进一步探讨具体场景的指令实现,可以随时提出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值