vue3+vite+ts 自定义指令详解

发布于:2024-05-09 ⋅ 阅读:(24) ⋅ 点赞:(0)
  1. directive-自定义指令(属于破坏性更新)

    Vue中有v-if,v-for,v-bind,v-show,v-model 等等一系列方便快捷的指令 今天一起来了解一下vue里提供的自定义指令

Vue3指令的钩子函数
created 元素初始化的时候
beforeMount 指令绑定到元素后调用 只调用一次
mounted 元素插入父级dom调用
beforeUpdate 元素被更新之前调用
update 这个周期方法被移除 改用updated
beforeUnmount 在元素被移除前调用
unmounted 指令被移除后调用 只调用一次
Vue2 指令 bind inserted update componentUpdated unbind

  1. 在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>) {
    el.style.background = dir.value.background;
    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.生命周期钩子参数详解

  • 第一个 el 当前绑定的DOM 元素

  • 第二个 binding

    instance:使用指令的组件实例。
    value:传递给指令的值。例如,在 v-my-directive=“1 + 1” 中,该值为 2。
    oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否有更改都可用。
    arg:传递给指令的参数(如果有的话)。例如在 v-my-directive:foo 中,arg 为 “foo”。
    modifiers:包含修饰符(如果有的话) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}。
    dir:一个对象,在注册指令时作为参数传递。例如,在以下指令中

  • 第三个 当前元素的虚拟DOM 也就是Vnode

  • 第四个 prevNode 上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用

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

<template>
   <div>
      <input v-model="value" type="text" />
      <DVue v-move="{ background: value }"></DVue>
   </div>
</template>
   
<script setup lang='ts'>
import DVue from './components/D.vue'
import { ref, Directive, DirectiveBinding } from 'vue'
let value = ref<string>('')
type Dir = {
   background: string
}
const vMove: Directive = (el, binding: DirectiveBinding<Dir>) => {
   el.style.background = binding.value.background
}
</script>

5.示例Demo
1.按钮权限按钮

<template>
    <div style="margin-top: 20px">
        <el-button v-has-show="'shop:create'">创建</el-button>
        <el-button v-has-show="'shop:edit'">编辑</el-button>
        <el-button v-has-show="'shop:delete'">删除</el-button>
    </div>
</template>
<script setup lang="ts">
import type { Directive } from "vue"
localStorage.setItem("userId", "myYYDS")
// mock后台返回的数据
const permission = [
    "myYYDS:shop:edit",
    "myYYDS:shop:create",
    "myYYDS:shop:delete",
]
const userId = localStorage.getItem("userId") as string
const vHasShow: Directive<HTMLElement, string> = (el, bingding) => {
    if (!permission.includes(userId + ":" + bingding.value)) {
        el.style.display = "none"
    }
}
</script>
<style scoped lang="scss"></style>

  1. 移动 自定义拖拽指令
    在这里插入图片描述
<template>
    <div style="margin-top: 20px" v-move class="box">
        <div class="header"></div>
        <div>我移动啦</div>
    </div>
</template>
<script setup lang="ts">
import type { Directive } from "vue"
const vMove: Directive<any, void> = (el: HTMLElement) => {
    let moveElement: HTMLDivElement = el.firstElementChild as HTMLDivElement
    const mouseDown = (e: MouseEvent) => {
        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"
        }
        document.addEventListener("mousemove", move)
        document.addEventListener("mouseup", () => {
            document.removeEventListener("mousemove", move)
        })
    }
    moveElement.addEventListener("mousedown", mouseDown)
}
</script>
<style scoped lang="scss">
.box {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 200px;
    height: 200px;
    border: 1px solid #ccc;
    .header {
        height: 20px;
        background: black;
        cursor: move;
    }
}
</style>

3.图片懒加载

<template>
    <div v-for="item in arr">
        <img height="500" :data-index="item" v-lazy="item" width="360" alt="" />
    </div>
</template>
<script setup lang="ts">
import { Directive } from "vue"

// vite 提供的方法可以引入全部的图片
// glob:是懒加载的模式
// globEager 静态加载全部
let imageList: Record<string, { default: string }> = import.meta.glob(
    "../../assets/images/*.*",
    { eager: true },
)
let arr = Object.values(imageList).map((v) => v.default)

let vLazy: Directive<HTMLImageElement, string> = async (el, bingding) => {
    const def = await import("../../assets/vue.svg")
    el.src = def.default
    // 判断元素是否是可视区域
    const observer = new IntersectionObserver((enr) => {
        if (enr[0].intersectionRatio) {
            setTimeout(() => {
                el.src = bingding.value
            }, 2000)
        }
    })
    observer.observe(el)
}
</script>
<style scoped lang="scss"></style>

在这里插入图片描述
在这里插入图片描述