除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外,Vue 还允许你注册自定义的指令,一个自定义指令被定义为一个包含类似于组件的生命周期钩子的对象。钩子接收指令绑定到的元素。

1、Vue3指令的钩子函数

  • created 元素初始化的时候
  • beforeMount 指令绑定到元素后调用 只调用一次
  • mounted 元素插入父级dom调用
  • beforeUpdate 元素被更新之前调用
  • update 这个周期方法被移除 改用updated
  • beforeUnmount 在元素被移除前调用
  • unmounted 指令被移除后调用 只调用一次
const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount() {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted() {},
  // 绑定元素的父组件更新前调用
  beforeUpdate() {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated() {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount() {},
  // 绑定元素的父组件卸载后调用
  unmounted() {}
}

2、在setup 内部使用自定义指令

在 <script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。在上面的例子中,vFocus 即可以在模板中以 v-focus 的形式使用。

<template>
  <div>
    <button @click="flag = !flag">切换</button>

    <div v-if="flag" v-move:abc.long="{ background: 'green' }" class="zhidingyi">自定义指令</div>
    <br><br>
    <!-- 修改flag值会触发beforeUpdate updated -->
    <div v-move:abc.long="{ background: 'red', flag: flag }" class="zhidingyi">自定义指令22</div>

  </div>
</template>

<script setup lang="ts">
import { ElementNode } from '@vue/compiler-core';
import { Directive, DirectiveBinding } from 'vue';
let flag = ref(true)
let vMove: Directive = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
    console.log("createdcreatedcreatedcreated", el, binding, vnode, prevVnode)

  },
  // 在元素被插入到 DOM 前调用
  beforeMount() {
    console.log("beforeMountbeforeMountbeforeMount")
  },
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  // mounted(...arg) {
  //   // el: HTMLElement, binding: DirectiveBinding
  //   console.log("mountedmounted", arg)
  //   arg[0].style.color = '#fff'
  // },
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    console.log("mountedmounted", el, binding)
    if (binding.modifiers.long) {
      console.log("修饰符long", binding.modifiers.long)
    }
    el.style.background = binding.value.background
    el.style.fontSize = "24px"
  },
  // 绑定元素的父组件更新前调用
  beforeUpdate() {
    console.log("beforeUpdatebeforeUpdate")

  },
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated() {
    console.log("updatedupdated")

  },
  // 绑定元素的父组件卸载前调用
  beforeUnmount() {
    console.log("beforeUnmountbeforeUnmountbeforeUnmount")
  },
  // 绑定元素的父组件卸载后调用
  unmounted() {
    console.log("unmountedunmountedunmountedunmounted")

  }
}

</script>

<style scoped>
</style>

在模板中使用

export default {
  setup() {
    /*...*/
  },
  directives: {
    // 在模板中启用 v-focus
    focus: {
      /* ... */
    }
  }
}

注册全局使用

const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
  /* ... */
})

3、钩子参数

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。

  • binding:一个对象,包含以下 property。

    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。

  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

4、简写自定义指令

对于自定义指令来说,需要在 mounted 和 updated 上实现相同的行为、又并不关心其他钩子的情况很常见。此时我们可以将指令定义成一个下面这样的函数:

<template>
  <div>
    <h1>自定义指令</h1>
    <input type="text" v-model="inputVal">
    <br>
    <br>
    <div class="div" v-background="{ background: inputVal }">{{ inputVal }}</div>
  </div>
</template>

<script setup lang="ts">
import { DirectiveBinding } from 'vue';
let inputVal = ref('')
type Binding = {
  background: string
}

let vBackground = (el: HTMLElement, binding: DirectiveBinding<Binding>) => {
  console.log("fffff", binding)
  el.style.background = binding.value.background
}

</script>

<style scoped>
.div {
  width: 300px;
  height: 300px;
  border: 1px solid #000;
}
</style>

5、实现一个拖动元素的自定义指令

<template>
  <div class="box" v-move>
    <div class="move"></div>
    <div class="move2"></div>
  </div>
</template>

<script setup lang="ts">
import { Directive, DirectiveBinding } from 'vue';

const vMove: Directive<any, void> = (el: HTMLElement, binding: DirectiveBinding) => {
  //  选择拖动触发元素
  let moveElement: HTMLDivElement = el.querySelector('.move2') as HTMLDivElement

  console.log('*-----*', moveElement)
  const mousedown = (e: MouseEvent) => {
    // 鼠标按下位置 距离元素顶边 左边距离
    let X = e.clientX - el.offsetLeft;
    let Y = e.clientY - el.offsetTop;
    const move = (e: MouseEvent) => {
      console.log(e)
      // 改变left top 减去鼠标距离元素边距
      el.style.left = e.clientX - X + 'px'
      el.style.top = e.clientY - Y + 'px'
    }
    document.addEventListener('mousemove', move)
    // 监听鼠标释放  停止移动监听
    document.addEventListener('mouseup', () => {
      document.removeEventListener('mousemove', move)
    })

  }
  // 监听拖动触发
  moveElement.addEventListener('mousedown', mousedown)
}

</script>

<style scoped lang="less">
.box {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 200px;
  border: 1px solid #000;
  background-color: skyblue;
  cursor: pointer;
  z-index: 999;

  .move {
    height: 30px;
    width: 100%;
    background-color: red;

  }

  .move2 {
    height: 40px;
    width: 100%;
    background-color: blue;
    cursor: move;
  }

}
</style>

Logo

前往低代码交流专区

更多推荐