18.Vue3 Directive自定义指令

427 阅读2分钟

Vue中有很多方便快捷的指令,今天来学习一下Vue3的自定义指令。 在Vue3中自定义指令属于破坏性更新,和Vue2有了很明显的区别。

Vue3指令的钩子函数

  1. created 元素初始化的时候
  2. beforeMount 指令绑定到元素后调用 只调用一次
  3. mounted 元素插入父级dom调用
  4. beforeUpdate 元素被更新之前调用
  5. update 这个周期方法被移除 改用updated
  6. beforeUnmount 在元素被移除前调用
  7. unmounted 指令被移除后调用 只调用一次

定义全局指令

const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})

如果想注册局部指令,组件中也接受一个 directives 

directives: {
  focus: {
    // 指令的定义
    mounted(el) {
      el.focus()
    }
  }
}

Vue3 setup

但这里有一个需要注意的限制:必须以 vNameOfDirective 的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。

<template>
  <button @click="show = !show">开关{{show}} ----- {{title}}</button>
  <Dialog  v-move-directive="{background:'green',flag:show}"></Dialog>
</template>
 
const vMoveDirective: Directive = {
  created: () => {
    console.log("初始化====>");
  },
  beforeMount(...args: Array<any>) {
    // 在元素上做些操作
    console.log("初始化一次=======>");
  },
  mounted(el: any, dir: DirectiveBinding<Value>) {
    console.log("初始化========>");
  },
  beforeUpdate() {
    console.log("更新之前");
  },
  updated() {
    console.log("更新结束");
  },
  beforeUnmount(...args: Array<any>) {
    console.log(args);
    console.log("======>卸载之前");
  },
  unmounted(...args: Array<any>) {
    console.log(args);
    console.log("======>卸载完成");
  },
};

3.生命周期钩子参数详解

1.el

当前指令绑定的DOM元素

2.binding
  1. instance 使用指令的组件实例。
  2. value:传递给指令的值。例如,在 v-my-directive="1 + 1" 中,该值为 2。
  3. oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否有更改都可用。
  4. arg:传递给指令的参数(如果有的话)。例如在 v-my-directive:foo 中,arg 为 "foo"。
  5. modifiers:包含修饰符(如果有的话) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}。
  6. dir:一个对象,在注册指令时作为参数传递。里面保存着指令的生命周期函数。
3.Vnode

当前元素的虚拟DOM

4.prevNode

上一个虚拟节点 仅在 beforeUpdateupdated 钩子中可用

4.函数简写

你可能想在 mountedupdated 时触发相同行为,而不关心其他的钩子函数。那么你可以通过将这个函数模式实现

<template>
   <div>
      <input v-model="value" type="text" />
      <A v-move="{ background: value }"></A>
   </div>
</template>
   
<script setup lang='ts'>
import A from './components/A.vue'
import { ref, Directive, DirectiveBinding } from 'vue'
let value = ref<string>('')
type Dir = {
   background: string
}
// 简写在 mounted 和 updated 时调用
const vMove: Directive = (el, binding: DirectiveBinding<Dir>) => {
   el.style.background = binding.value.background
}
</script>

案例:自定义拖拽指令

<template>
  <div v-move class="box">
    <div class="header">拖拽盒子</div>
  </div>
</template>
 
<script setup lang='ts'>
import { Directive } from "vue";
const vMove: Directive = {
  mounted(el: HTMLElement) {
    // 断言为DOM元素
    let moveEl = el.firstElementChild as HTMLElement;
    // 鼠标按下事件
    const mouseDown = (e: MouseEvent) => {
      // 鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最边的距离
      console.log(e.clientX, e.clientY, "-----起始", el.offsetLeft);
      let X = e.clientX - el.offsetLeft;
      let Y = e.clientY - el.offsetTop;
      // 鼠标移动事件
      const move = (e: MouseEvent) => {
        el.style.left = e.clientX - X + "px";
        el.style.top = e.clientY - Y + "px";
        console.log(e.clientX, e.clientY, "---改变");
      };
      // 注册鼠标移动事件
      document.addEventListener("mousemove", move);
      // 监听鼠标抬起
      document.addEventListener("mouseup", () => {
        // 清除移动事件Ï
        document.removeEventListener("mousemove", move);
      });
    };
    // 注册鼠标按下事件
    moveEl.addEventListener("mousedown", mouseDown);
  },
};
</script>
 
 
<style lang="scss">
.box {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 200px;
  border: 1px solid #ccc;
  .header {
    height: 30px;
    text-align: center;
    line-height: 30px;
    background: pink;
    cursor: move;
  }
}
</style>