Vue 3 自定义指令

在 Vue 3 中,自定义指令允许你对 DOM 元素进行低级操作。当内置的指令如 v-ifv-forv-model 无法满足你的需求时,可以使用自定义指令。

自定义指令通过 app.directive() 方法注册,指令的生命周期钩子函数让你可以对 DOM 进行详细的操作。

1. 自定义指令的基本使用

自定义指令的基本结构包括:

  • 指令名称:可以是任何符合 JavaScript 变量命名规则的字符串。
  • 钩子函数:每个指令都有一组钩子函数来响应不同的生命周期阶段。

1.1. 注册全局指令

你可以在 Vue 应用的创建过程中全局注册一个自定义指令。

// main.js
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// 注册全局自定义指令
app.directive('focus', {
  mounted(el) {
    el.focus(); // 当元素挂载时,自动聚焦
  }
});

app.mount('#app');

在这个例子中,我们注册了一个名为 v-focus 的全局指令,它会在元素挂载时自动聚焦。

<template>
  <div>
    <input v-focus />
  </div>
</template>

当页面加载时,<input> 元素会自动获得焦点。

1.2. 注册局部指令

你也可以在单个组件中注册局部指令,只在该组件中有效。

<template>
  <div>
    <input v-focus />
  </div>
</template>

<script>
export default {
  directives: {
    focus: {
      mounted(el) {
        el.focus(); // 当元素挂载时,自动聚焦
      }
    }
  }
};
</script>

在这个例子中,v-focus 指令仅在该组件中有效。

2. 自定义指令的钩子函数

每个自定义指令都有多个生命周期钩子函数,允许你在不同的阶段处理 DOM 元素。

2.1. 常见的钩子函数

  • beforeMount:在指令绑定到元素之前调用。
  • mounted:在指令绑定到元素后立即调用。
  • beforeUpdate:当所在的组件的 VNode 更新之前调用。
  • updated:当所在的组件的 VNode 更新之后调用。
  • beforeUnmount:当指令解绑之前调用。
  • unmounted:当指令解绑后调用。

2.2. 使用钩子函数

app.directive('color', {
  beforeMount(el, binding) {
    // 在元素绑定之前设置颜色
    el.style.color = binding.value;
  },
  updated(el, binding) {
    // 当绑定的值更新时,更新颜色
    el.style.color = binding.value;
  }
});

在这个例子中,v-color 指令用于根据传入的值动态修改元素的文本颜色。binding.value 包含传递给指令的值。

<template>
  <div>
    <p v-color="'red'">This is red text.</p>
    <p v-color="'blue'">This is blue text.</p>
  </div>
</template>

3. 指令参数和修饰符

指令可以接受参数和修饰符来增加其功能。参数用于指令的额外配置,修饰符则是某些指令的附加标志。

3.1. 指令参数

指令参数允许你在指令中传递动态参数。参数使用 : 来绑定。

<template>
  <div>
    <p v-color:background="'yellow'">This background is yellow.</p>
  </div>
</template>

<script>
export default {
  directives: {
    color: {
      mounted(el, binding) {
        if (binding.arg === 'background') {
          el.style.backgroundColor = binding.value;
        }
      }
    }
  }
};
</script>

在这个例子中,v-color:background 作为指令的参数 arg 被传递,它用于动态修改元素的背景颜色。

3.2. 指令修饰符

修饰符通常是以 . 开头,用于扩展指令的功能。

<template>
  <div>
    <input v-focus.prevent />
  </div>
</template>

<script>
export default {
  directives: {
    focus: {
      mounted(el, binding) {
        if (binding.modifiers.prevent) {
          // 自定义行为:当添加 .prevent 修饰符时不聚焦
          el.blur();
        } else {
          el.focus();
        }
      }
    }
  }
};
</script>

在这个例子中,v-focus.prevent 表示如果添加了 .prevent 修饰符,输入框将不获得焦点。

4. 使用 binding 对象

指令的钩子函数接收一个 binding 对象,它提供了指令的信息:

  • binding.value:传递给指令的值。
  • binding.arg:指令的参数(如 v-color:background 中的 background)。
  • binding.modifiers:指令的修饰符(如 .prevent)。
  • binding.oldValue:指令绑定值的旧值,仅在 updatedbeforeUpdate 钩子中有效。
  • binding.instance:当前组件实例。
  • binding.el:指令绑定的 DOM 元素。

5. 通过指令实现动态样式

你可以通过指令动态改变元素的样式或属性。例如,创建一个 v-tooltip 指令来为元素添加工具提示。

app.directive('tooltip', {
  mounted(el, binding) {
    const tooltip = document.createElement('span');
    tooltip.textContent = binding.value;
    tooltip.style.position = 'absolute';
    tooltip.style.visibility = 'hidden';
    tooltip.style.backgroundColor = '#000';
    tooltip.style.color = '#fff';
    tooltip.style.padding = '5px';
    tooltip.style.borderRadius = '3px';
    el.style.position = 'relative';
    el.appendChild(tooltip);

    el.addEventListener('mouseenter', () => {
      tooltip.style.visibility = 'visible';
    });

    el.addEventListener('mouseleave', () => {
      tooltip.style.visibility = 'hidden';
    });
  }
});

<template>
  <div>
    <button v-tooltip="'This is a tooltip'">Hover over me</button>
  </div>
</template>

在这个例子中,v-tooltip 指令会在元素上创建一个工具提示,并在鼠标悬停时显示。

6. 总结

  • 注册指令:你可以在 Vue 3 中通过 app.directive() 来注册全局或局部的自定义指令。
  • 生命周期钩子:每个指令都可以定义多个生命周期钩子,如 mountedbeforeUpdateupdated 等,用于操作 DOM 元素。
  • 参数与修饰符:指令支持传递参数(如 v-color:background)和修饰符(如 .prevent)来增加功能。
  • binding 对象:通过 binding 对象可以获取指令的值、参数、修饰符以及更多信息,帮助你实现复杂的指令逻辑。

自定义指令使 Vue 3 更加灵活,可以扩展内置的指令功能,适应复杂的开发需求。