vue3_自定义指令directive

1.自定义指令注册方式

  • 在 <script setup>

任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令

// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
  • 没有使用 <script setup> 的情况下
export default {
  setup() {
    /*...*/
  },
  directives: {
    // 在模板中启用 v-focus
    focus: {
      /* ... */
    }
  }
}
  • 全局注册
const app = createApp({})

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

2.指令钩子 及 钩子参数

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

钩子参数:

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

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

    • 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 钩子中可用。

3.常用指令的示例:拖拽,图片懒加载,权限按钮

  • 拖拽
//父组件:
<template>
    <div class="container">
        <drag v-drag></drag>
    </div>
</template>

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

import { ref } from 'vue'
import drag from './drag.vue'

const range = (loc: number, min: number, max: number) => {
    if (loc > max) {
        return max;
    } else if (loc < min) {
        return min;
    } else {
        return loc;
    }
}
const vDrag: Directive = {
    mounted(el: HTMLElement) {
        let moveEl = el as HTMLElement
        const moveDown = (e: MouseEvent) => {
            let X = e.clientX - el.offsetLeft
            let Y = e.clientY - el.offsetTop
            const move = (e: MouseEvent) => {
                let w = e.clientX - X
                let h = e.clientY - Y
                let winWidth = document.documentElement.clientWidth
                let winHeight = document.documentElement.clientHeight
                w = range(w, 0, winWidth - el.offsetWidth)
                h = range(h, 0, winHeight - el.offsetHeight)
                el.style.left = w + 'px'
                el.style.top = h + 'px'
            }
            document.addEventListener('mousemove', move)
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', move)
            })
        }
        moveEl.addEventListener('mousedown', moveDown)

    }

}
</script>

//拖拽子组件
<template>
 <div class="box">
 </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'
</script>

<style scoped lang="less">
.box {
  position: absolute;
  width: 200px;
  height: 200px;
  border: 1px solid #ccc;
}
</style>
  • 图片懒加载

IntersectionObserver 可以用来自动监听元素是否进入了设备的可视区域之内,官方文档解释:https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver

<template>
    <div class="container">
        <div class="conimg">
            <div class="img" v-for="item in arr">
                <img  :data-index="item" alt="" v-lazy="item">
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { Directive, DirectiveBinding, onBeforeUpdate } from 'vue';
import { ref } from 'vue'
const images: Record<string, { default: string }> = import.meta.glob('/src/assets/image/*.jpg')
// const images: Record<string, { default: string }> = import.meta.globEager('/src/assets/image/*.jpg')
let arr = Object.keys(images)
// let arr = Object.values(images).map(v => v.default)

let vLazy: Directive<HTMLImageElement, string> = async (el, binding) => {
    let url = await import('../../../public/vite.svg')
    el.src = url.default
    let observer = new IntersectionObserver((entires) => {
        console.log(entires);
        if(entires[0].intersectionRatio >0 && entires[0].isIntersecting){
            setTimeout(()=>{
                el.src = binding.value
                observer.unobserve(el)
            },4000)
        }
    })
    observer.observe(el)

}

}
</script>

<style scoped>
.conimg{
    width: 200px;
    height: 600px;
    border:1px solid ;
    overflow: auto;
}
.img {
    width: 200px;
    height: 200px;
}
img{
    width: 100%;
    height: 100%;
}
</style>
  • 权限按钮

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

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

import { ref } from 'vue'
localStorage.setItem('uId', 'xxx')
// 后端返回
const permission = [
    'xxx:shop:edit',
    // 'xxx:shop:create',
    // 'xxx:shop:delete',
]
const uid = localStorage.getItem('uId')
const vHasShow: Directive<HTMLElement, string> = (el, bingding) => {
    if (!permission.includes(uid + ':' + bingding.value)) {
        el.style.display = 'none'
    }
}

</script>

 分享读过的写的较清晰的文章

https://juejin.cn/post/7107477387578703885

vue官网

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值