Vue3 语法补充:自定义指令

227 阅读3分钟

自定义指令

什么是自定义指令

在 Vue 的模板语法中我们学习过各种各样的指令:v-show、v-for、v-model 等等,除了使用这些指令之外,Vue 也允许我们来自定义自己的指令。

通常在某些情况下,你需要对 DOM 元素进行底层操作,这个时候就会用到自定义指令。

自定义指令分为 局部自定义指令全局自定义指令

局部自定义指令

局部自定义指令就是只能在当前组件内使用的自定义指令,通过 directives 选项来定义:

export default {
  directives: {
    focus: {
      mounted(el, bindings, vnode, preVnode) {
        console.log(el, bindings, vnode, preVnode)
        el.focus()
      }
    }
  },

  setup() {}
}
<!-- 指令的使用 -->
<input type="text" v-focus />

注:不可以在 setup 函数中定义指令。

全局自定义指令

全局自定义指令可以在任意组件内使用,通过 vue 实例的 directive 方式来定义:

main.js 文件中:

const app = createApp(App);

app.directive("focus", {
  mounted(el, bindings, vnode, preVnode) {
    console.log(el, bindings, vnode, preVnode);
    el.focus();
  },
});

自定义指令的生命周期

对于使用了指令的元素,Vue 提供了以下的钩子函数:

  • created:在元素的属性及事件监听应用之前调用;
  • beforeMount:指令绑定到元素后,且父组件挂载之前调用;
  • mounted:绑定元素的父组件挂载后调用;
  • beforeUpdate:
  • updated:
  • beforeUnmount:绑定元素的父组件卸载前调用;
  • unmounted:绑定元素的父组件卸载时调用##

自定义指令的参数跟修饰符

在使用自定义指令时可以添加修饰符及接收参数:

<input type="text" v-focus.abc.def="'caohan'" />

abcdef 都是修饰符,也就是说修饰符可以不止一个。'caohan' 就是接收的参数的值。在自定义指令的生命周期钩子函数中,可以获取到在该元素上使用的修饰符及传过来的参数:

directives: {
    focus: {
      created(el, bindings) {
        console.log(bindings.modifiers) // { abc: true, def: true }
        console.log(bindings.value) // caohan
      },
    }
  },

修饰符是以对象的形式返回,且值始终为 true。参数的值可以是字符串数值布尔值,也可以是对象或数组。

自定义指令的时间戳格式化案例

将返回的时间戳数据转换成具体格式化的时间来展示,除了可以使用计算属性 computedmethods 外,还可以通过自定义指令来完成。

代码实现

<template>
  <div>
    <h2 v-format-time="'YYYY/MM/DD'">1666452738</h2>
  </div>
</template>
// 使用第三方的库来实现格式化功能
import dayjs from 'dayjs'

export default {
  directives: {
    'format-time': {
      created(el, bindings) {
        // 将初始化数据的逻辑抽取到 created 里面
        // 添加一个属性来保存默认的格式
        bindings.formatString = 'YYYY-MM-DD HH:mm:ss'
        if (bindings.value) {
          bindings.formatString = bindings.value
        }
      },
      mounted(el, bindings) {
        const textContent = el.textContent
        let timestamp = parseInt(textContent)
        if (textContent.length === 10) {
          // 如果时间戳是以秒为单位就转化成以毫秒为单位
          timestamp *= 1000
        }
        el.textContent = dayjs(timestamp).format(bindings.formatString)
      }
    }
  }
}

如果自定义的指令传入了参数,那么就按照传入的参数格式来展示,如果没有传参,就按照默认的格式来展示。

全局自定义指令实现

因为上面的功能是一个公共的需求,所以应该定义为一个全局的自定义指令。

main.js 文件中:

import dayjs from 'dayjs'

const app = createApp(App);

app.directive("format-time", {
  created(el, bindings) {
    // 将初始化数据的逻辑抽取到 created 里面
    bindings.formatString = 'YYYY-MM-DD HH:mm:ss'
    if (bindings.value) {
      bindings.formatString = bindings.value;
    }
  },
  mounted(el, bindings) {
    const textContent = el.textContent;
    let timestamp = parseInt(textContent);
    if (textContent.length === 10) {
      // 如果时间戳是以秒为单位就转化成以毫秒为单位
      timestamp *= 1000;
    }
    el.textContent = dayjs(timestamp).format(bindings.formatString);
  },
});

抽取全局指令实现的逻辑

一、直接抽取

main.js 中:

import registerDirectives from "./directives/index.js";

registerDirectives(app);

/directives/index.js 中:

import registerFormatTime from "./format-time.js";

export default function (app) {
  registerFormatTime(app);
}

/directives/format-time.js 中:

import dayjs from "dayjs";

export default function (app) {
    app.directive("format-time", {
      created(el, bindings) {
        // 将初始化数据的逻辑抽取到 created 里面
        bindings.formatString = 'YYYY-MM-DD HH:mm:ss'
        if (bindings.value) {
          bindings.formatString = bindings.value;
        }
      },
      mounted(el, bindings) {
        const textContent = el.textContent;
        let timestamp = parseInt(textContent);
        if (textContent.length === 10) {
          // 如果时间戳是以秒为单位就转化成以毫秒为单位
          timestamp *= 1000;
        }
        el.textContent = dayjs(timestamp).format(bindings.formatString);
     },
}

/directives/index.js 中我们进行全局自定义指令的管理。

二、使用插件注册抽取