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>
分享读过的写的较清晰的文章