Vue3-自定义指令的使用

1,025 阅读4分钟

Vue3-自定义指令的使用

在Vue中有很多内置的指令,如:v-ifv-model等等,除了这些内置的指令外,Vue还允许我们自己自定义指令。自定义指令主要是为了操作普通元素的底层dom,提高复用性。(不建议在组件上使用自定义指令) 一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

1. 基本使用

注册自定义指令接受两个参数,1:指令名称,2:配置对象。(配置对象是一个类似于组件的生命周期钩子的对象

定义和注册:

app.directive("focus", {
    mounted(el: HTMLInputElement) {
        el.focus();
    },
});

​ 上面的例子就是注册了一个全局的focus指令,这个指令会在DOM挂载后将input元素聚焦。

使用:

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

​ 全局注册,在所有的地方都可用,使用的方式:v-注册的名称。如上面注册的指令名称是focus,使用:v-focus

2.自定义指令的配置对象

配置对象是一个类似于组件的生命周期钩子的对象,里面有mountedupdated等函数,会在组件的对应的生命周期中调用。

{
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
    
  // 在元素被插入到 DOM 前调用
  beforeMount() {},
    
  // 在绑定元素的父组件,以及他自己的所有子节点都挂载完成后调用
  mounted() {},
    
  // 绑定元素的父组件更新前调用
  beforeUpdate() {},
    
  // 在绑定元素的父组件,以及他自己的所有子节点都更新后调用
  updated() {},
    
  // 绑定元素的父组件卸载前调用
  beforeUnmount() {},
    
  // 绑定元素的父组件卸载后调用
  unmounted() {}
  }
}

这些钩子函数都接受同样的参数:

  • el:指令绑定的元素的DOM。
  • binding:一个传参对象,包含以下的属性:
    • value:传递给指令的值。例如:v-focus="1",获取到的值就是 1
    • oldValue:更新之前的值,仅在 beforeUpdateupdated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:传入修饰符的对象 。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。
  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdateupdated 钩子中可用。

举个例子,像下面这样使用指令:

<div v-example:foo.bar="baz">

binding 参数会是一个这样的对象:

{
  arg: 'foo',
  modifiers: { bar: true },//传递的修饰符
  value: /* `baz` 的值 */,
  oldValue: /* 上一次更新时 `baz` 的值 */
}

3.简化使用 mountedupdated

对于自定义指令来说,需要在 mountedupdated 上实现相同的行为、又并不关心其他钩子的情况很常见。例如下面的例子:

app.directive("focus", {
    mounted(el: HTMLInputElement, bing: DirectiveBinding) {
        if (bing.value) {
            el.focus();
        }
    },
    updated(el: HTMLInputElement, bing: DirectiveBinding) {
        if (bing.value) {
            el.focus();
        }
    },
});

​ 定义了一个自动聚焦input的指令,在组件挂载完成后,和组件更新后都去聚焦input元素。

可以简写成下面的例子:

app.directive("focus", (el: HTMLInputElement, bing: DirectiveBinding) => {
    if (bing.value) {
        el.focus();
    }
});

​ 不再传入一个配置对象,而是传入一个函数,这个函数会在组件的mountedupdated周期都执行。

4. 指定传入值的类型

在定义指令得时候,可以通过泛型来指定传入值的类型。

定义传入值的类型:

app.directive("focus", (el: HTMLInputElement, bing: DirectiveBinding<Person>) => {
    el.focus();
    console.log(bing.value.name);
});

使用:

<script setup lang="ts">
const reactive1 = reactive<Person>({
   age: 0,
   name: "tom"
});
</script>
<input v-focus="reactive1">

​ 参数DirectiveBinding指定泛型,指定的泛型就是传入的值的泛型。

5. 在组件上使用

当指令在组件上使用的时候,会因为组件的透传特征,流入并作用在组件内的根节点元素上。但是如果组件有多个根节点元素,那么指令就会被忽略不起作用,并抛出一个警告。

不同于其他的透传属性,指令不能通过 v-bind="$attrs" 来指定绑定给某个元素,因此不推荐在组件上使用。

关于Vue3中的透传,可以阅读这篇文章:一文搞懂Vue3中的透传属性 - 掘金 (juejin.cn)