初探vue3 - 自定义指令

458 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

前言

学习vue3有一段时间了,也用vue3写了几个项目,今天抽空整理一下关于vue3的自定义指令知识,写文章的初衷是,加深自己的记忆、理解,方便以后复习、查阅。

内置指令与自定义指令

vue本身内置了一系列的指令(如:v-modelv-ifv-show),除了这些内置指令以外,还允许自己注册指令,也就是自定义指令。
指令的作用主要也是复用代码,它与组件组合式函数的不同是,指令主要处理的是涉及DOM元素访问的逻辑;而组件主要是代码模块化;组合式函数主要是侧重代码逻辑的封装。

自定义指令的使用方式

使用指令之前,都需要先注册,分全局注册和局部注册。

  1. 全局注册:在所有的页面都可以使用
// main.ts
const app = createApp()
app.directive('xxx', {})

// <div v-xxx></div> // 在页面中使用
  1. 局部注册:分两种,一种是使用Option API方式,另一种是使用Composition API,只在当前页面有效。 在option api中,其定义方式和vue2是完全一致的
// option api 和vue2一样
export default {
  directives: {
    xxx: {}
  }
}
// <div v-xxx></div> // 在页面中使用

composition api ,在<script setup>中,任何以v开头的驼峰形式命名的变量,都可以用作指令。

// composition api
<script setup lang='ts'>
  const vXxx = {/**/}
</script>

// <div v-xxx></div> // 在页面中使用

vue3自定义指令的变化

  1. 生命周期钩子:vue3的指令生命周期向组件靠拢,两者越发的像了,也更加的便于理解;看下具体的变化。
  // vue3
  const vXxx = {
    // 在绑定元素的 attribute 前
    // 或事件监听器应用前调用
    created(el, binding, vnode, prevVnode) {},
    // 在元素被插入到 DOM 前调用
    beforeMount() {},
    // 在绑定元素的父组件
    // 及他自己的所有子节点都挂载完成后调用
    mounted() {},
    // 绑定元素的父组件更新前调用
    beforeUpdate() {},
    // 在绑定元素的父组件
    // 及他自己的所有子节点都更新后调用
    updated() {},
    // 绑定元素的父组件卸载前调用
    beforeUnmount() {},
    // 绑定元素的父组件卸载后调用
    unmounted() {}
  }
  
  // vue2
  const vXxx2 = {
    // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
    bind(el, binding, vnode, oldVnode) {},
    // 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
    inserted() {},
    // 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前
    // 指令的值可能发生了改变,也可能没有。
    // 但是你可以通过比较更新前后的值来忽略不必要的模板更新
    update() {},
    // 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    componentUpdated() {},
    // 只调用一次,指令与元素解绑时调用。
    unbind() {},
  }

通过上面的对比,就可以很清楚的看出来,vue3重写了指令的生命周期钩子,这更加的合理,使用起来和组件的差不多,减少心智负担。

  1. 钩子函数参数:vue3除了重写了生命周期钩子函数,还对函数参数做了一些小调整,主要是binding对象的更改。
  • 去除了name属性,表示指令名,不包括 v- 前缀。
  • 去除了expression属性,表示字符串形式的指令表达式。例如 v-xxx="1 + 1" 中,表达式为 "1 + 1"
  • 新增instance属性,表示使用该指令的组件实例
  • 新增dir属性,表示指令的定义对象

注意事项

vue3中因为支持一个template下面可以同时有多个根节点,所以,如果指令用在一个多根节点的组件中,指令会不生效,并且会抛出一个错误。