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

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

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


文章目录

    • **Vue 2 与 Vue 3 自定义指令(Directive)详解**
      • **一、基本用法对比**
        • **1. Vue 2 中注册指令**
        • **2. Vue 3 中注册指令**
      • **二、指令生命周期钩子对比**
        • **Vue 2 指令钩子**
        • **Vue 3 指令钩子**
      • **三、核心差异详解**
        • **1. 钩子函数名称变化**
        • **2. 参数对象差异**
        • **3. 碎片(Fragment)支持**
      • **四、实战示例**
        • **场景:实现一个文本高亮指令 `v-highlight`**
          • **Vue 2 实现**
          • **Vue 3 实现**
      • **五、高级用法**
        • **1. 动态指令参数**
        • **2. 指令与 Composition API 结合(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 前(类似 bind el, binding, vnode
mounted 元素挂载到 DOM 后(类似 inserted el, binding, vnode
beforeUpdate 组件更新前(类似 update el, binding, vnode
updated 组件更新后 el, binding, vnode
beforeUnmount 组件卸载前(类似 unbind el, 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 是绑定的值,用来传递指令的具体数据。





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

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

你可能感兴趣的:(前端知识点,vue.js,前端,javascript)