Vue3自定义指令(directive)


前言

此文章主要讲了vue3中自定义指令的使用,以及一些WebAPI的使用。如 ResizeObserver、IntersectionObserver API的使用。


一、Vue3指令钩子函数

  • created 元素初始化
  • beforeMount 指令绑定到元素后调用 只调用一次
  • mounted 元素插入父级dom调用
  • beforeUpdate 元素被更新前调用
  • updated 元素被更新后调用
  • beforeUnmount 元素移除前调用
  • unmounted 元素被移除后调用

vue2中的指令钩子函数有:

bind、inserted、update、componentUpdated、unbind

二、自定义指令的两种方式

利用Directive可以创建自定义指令

1.局部使用

接收两个参数
el:表示当前组件实例
dir:表示传入的参数以及函数

DirectiveBinding:与返回参数一致,使用来约束类型

export interface DirectiveBinding<V = any> {
    instance: ComponentPublicInstance | null;
    value: V;
    oldValue: V | null;
    arg?: string;
    modifiers: DirectiveModifiers;
    dir: ObjectDirective<any, V>;
}

使用如下:

<script setup lang="ts">
import {Directive, DirectiveBinding} from "vue";
import A from "./A.vue";
type Dir = {
  background:string
}
const vMove:Directive = {
  created(){
    console.log('------created-------')
  },
  beforeMount(){
    console.log('------beforeMount-------')
  },
  mounted(el:HTMLElement,dir:DirectiveBinding<Dir>){
    console.log('------mounted-------')
    el.style.background = dir.value.background
  },
  beforeUpdate(){
    console.log('------beforeUpdate-------')
  },
  updated(){
    console.log('------updated-------')
  },
  beforeUnmount(){
    console.log('------beforeUnmount-------')
  },
  unmounted(){
    console.log('------unmounted-------')
  }
}
</script>

<template>
<A v-move:aaa.smz="{background:'red'}"/>
</template>

例子1:鉴权

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

// 用户id
localStorage.setItem('userId','smz')
// mock后台数据
const permission = [
  'smz:shop:edit',
  // 'smz:shop:create',
  'smz: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>

<template>
<div class="btns">
  <button v-has-show="'shop:create'">创建</button>
  <button v-has-show="'shop:edit'">编辑</button>
  <button v-has-show="'shop:delete'">删除</button>
</div>
</template>

例子2:拖拽

这里使用拖拽需要改变拖拽的position,因为不改变,则修改元素位置不起作用

static
该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。

<script setup lang="ts">
/**
 * Element.firstElementChild:只读属性,返回对象第一个子元素,没有则返回Null
 * Element.clientX:只读属性,元素距离视口左边的距离(中心点)
 * Element.offsetLeft:只读属性,元素左上角距离视口左边的距离
 * Element.offsetWidth:元素宽度
 * Element.offsetHeight:元素高度
 * window.innerWidth:可视窗宽度
 * window.innerHeight:可视窗高度
 */
import {Directive, DirectiveBinding} from "vue";

const vDrea:Directive<any,void> = (el:HTMLElement,binding:DirectiveBinding)=>{
  let gap = 10
  let moveElement:HTMLDivElement = el.firstElementChild as HTMLDivElement
  const mouseDown = (e:MouseEvent)=>{
    console.log(window.innerHeight)
    let X = e.clientX - el.offsetLeft
    let Y = e.clientY - el.offsetTop
    const move = (e:MouseEvent)=>{
      let x = e.clientX - X
      let y = e.clientY - Y
      //超出边界判断
      if (x<=gap){
        x = 0
      }
      if (y<=gap){
        y = 0
      }
      if (x>= window.innerWidth -el.offsetWidth -gap){
        x = window.innerWidth -el.offsetWidth
      }
      if (y>= window.innerHeight - el.offsetHeight-gap){
        y = window.innerHeight - el.offsetHeight
      }

      el.style.left = x + 'px'
      el.style.top = y + 'px'
    }
    // 鼠标移动
    document.addEventListener('mousemove',move)
    //松开鼠标
    document.addEventListener('mouseup',()=>{
      //清除移动事件
      document.removeEventListener('mousemove',move)
    })
  }
  //鼠标按下
  moveElement.addEventListener('mousedown',mouseDown)
}
</script>

<template>
  <div v-drea class="box">
    <div class="header"></div>
    <div>内容</div>
  </div>
</template>

<style lang="less" scoped>
.box{
  position: fixed;
  width: 300px;
  height: 250px;
  border: solid 1px black;
  .header{
    height: 30px;
    background-color: black;
  }
}
</style>

2.全局使用

定义好全局指令文件,其中需要导出指令钩子函数

/**
 * el:监听的dom元素
 * binding: 回调事件
 */
export default {
    mounted(el,binding) {
        //将dom与回调的关系塞入map
        map.set(el,binding.value)
        //监听el元素的变化
        ob.observe(el)
    },
    unmounted(el) {
        //取消监听
        ob.unobserve(el)
    }
}

在main.ts文件中添加以下代码
挂载指令,省略‘v-’前缀

import sizeDireect from '../src/directs/resize指令封装/sizeDireect'
app.directive('size-ob', sizeDireect)

使用:
在需要监听的标签上使用命令 v-size-ob="handle",其中handle为回调函数,其中返回的参数为尺寸信息

 <div class="dir"  v-size-ob="handle">

例子1:监听宽高指令

/**
 * @ResizeObserver 监听元素变化的API
 * @entries 元素变化的数组集合
 * @entry 每个被监听的元素 其中包含的属性有:
 *    borderBoxSize:边框盒尺寸
 *    contentBoxSize:内容盒尺寸
 *    contentRect:内容区域矩形信息 => DOMRectReadOnly {x: 0, y: 0, width: 3800, height: 3800, top: 0, …}
 *    devicePixelContentBoxSize:DPR尺寸
 *    target:哪一个元素发生变化
 */
const ob = new ResizeObserver((entries)=>{
    for (const entry of entries) {
        // 获取dom元素的回调
        const handler = map.get(entry.target)
        //存在回调函数
        if (handler){
            // 将监听的值给回调函数
            handler({
                width: entry.borderBoxSize[0].inlineSize,
                height: entry.borderBoxSize[0].blockSize
            })
        }
    }
})
/**
 * map:存储dom与回调函数的映射关系
 * 使用WeakMap,防止内存溢出
 */
const map = new WeakMap();
/**
 * el:监听的dom元素
 * binding: 回调事件
 */
export default {
    mounted(el,binding) {
        //将dom与回调的关系塞入map
        map.set(el,binding.value)
        //监听el元素的变化
        ob.observe(el)
    },
    unmounted(el) {
        //取消监听
        ob.unobserve(el)
    }
}

例子2:监听是否出现在视口

vite提供了批量导入的方法 import.meta.glob
eager:true表示静态导入

let imageList: Record<string,{default: string}> = import.meta.glob('../../../assets/images/*.*',{eager:true})
let arr = Object.values(imageList).map(v=>v.default)
/**
 * IntersectionObserver:监听元素与视口交叉的API
 * 返回一个监听集合,集合每一项有intersectionRatio表示在视口存在的比例
 */
export default {
    // @ts-ignore
     async mounted(el,binding){
         const def = await import('../../assets/vue.svg')
        el.src = def.default
         let ob = new IntersectionObserver((entries) => {
            if (entries[0].intersectionRatio >0){
                el.src = binding.valueOf()
                ob.unobserve(el)
            }
         })
        ob.observe(el)

    },
    unmounted(el){
    }
}

总结

此文章主要讲了vue3中自定义指令的使用,以及一些WebAPI的使用。如 ResizeObserver、IntersectionObserver API的使用

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天将降大任于我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值