1、自定义指令的作用:
自定义指令是用来操作底层DOM的,尽管vue推崇数据驱动视图的理念,但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和拓展,不仅仅可用于定义任何DOM操作,并且是可以重复使用。
自定义指令,是由一个包含类似组件生命周期钩子函数的对象组成,钩子函数会接受组件所绑定的元素作为参数
2、组件内的自定义指令:
在 中以 v 开头的驼峰形式命名的对象都可以看做是自定义指令
如:
<template>
<div class="cntainer">
<!-- 自定义指令 -->
<img class="img-box" v-img-rotote src="https://img.yzcdn.cn/vant/cat.jpeg" />
<div>测试自定义指令</div>
</div>
</template>
<script setup>
const vImgRotote = {
// 添加自定义指令,图片旋转动效
bind() {
console.log('bind')
},
update() {
console.log('update')
},
mounted(el) {
// el 当前绑定的元素对象
console.log('mounted', el)
el.addEventListener('mouseover', () => {
console.log('==mouseover==')
el.style.transform = 'rotate(360deg)'
el.style.transition = 'all 0.5s'
})
el.addEventListener('mouseleave', () => {
el.style.transform = 'rotate(0deg)'
// el.style.transition = 'all 0.5s'
})
}
}
</script>
或者直接 写指令js文件,通过文件引入
如:
import vImgRotote from './v-img-rotote.js' // 自定义的指令文件v-img-rotote.js
3、通常情况下我们会定义一个全局的自定义指令,方便使用
比如自定义防重提交指令
// 声明的防重提交指令文件,直接在main.ts 中引入并且挂载在vue实例上即可全局使用;
import { App, DirectiveBinding } from 'vue'
interface MyEl extends HTMLElement {
disabled?: boolean
}
export function setupMyReplayClickDirective(app: App) {
app.directive('myReplayClick', {
mounted(el: MyEl, binding: DirectiveBinding) {
el.addEventListener('click', () => {
if (!el.disabled) {
el.disabled = true
const timer = setTimeout(() => {
el.disabled = false
clearTimeout(timer)
}, binding.value || 1500)
}
})
},
// updated(el: MyEl, binding: DirectiveBinding) {
// console.log('=updated==el;', el)
// console.log('=updated==binding', binding)
// }
})
}
main.ts 中如下
import { App, createApp } from 'vue'
import aplication from './App.vue'
// const app = createApp({})
const app = createApp(aplication)
import { setupMyReplayClickDirective } from '@/directives/my-replay-click-directive.ts'
setupMyReplayClickDirective(app)
模板中使用如下:
<template>
<button v-my-replay-click="1000">提交</button>
// 因默认是1500毫秒,所以可以不写执行时间,直接写上指令即可
<button v-my-replay-click>提交</button>
</template>
如图:
4、自定义指令的生命周期钩子函数
// 此写法为在setup 中声明方式
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
// el: 要绑定的DOM元素,可以直接进行DOM操作
// binding: 一个对象,包含以下属性:
// value: 指令的值,如果传入了表达式,则为表达式的值,否则为 undefined,
比如v-my-replay-click="`${1000 + 500 }`" // 可以是变量参数,可以是具体的值
// oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用
// arg: 指令的参数,也即 v-bind:arg="value" 中的 arg
// modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
// instance:使用该指令的组件实例
// dir:指令的定义对象
// vnode: 代表绑定元素的底层 VNode:
// prevNode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
},
// 在元素被插入到 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) {}
}
通常情况下,我们在自定义指令时候,只需要在mounted 和updated上执行相关的逻辑,因此我们并不需要其他生命钩子函数,比如:
<div v-my-num=200>改变数字</div>
app.description('myNum', (el, bind) => {
console.log('====,el,bind)
el.innerText = `改变数字--${bind.value}`
})
**
5、组件上不建议使用自定义指令,因为自定义指令需要作用在根节点上,vue3组件中可能同时存在多个根节点;
**
仅代表自己观点,如有错误及不合适地方,欢迎批评指正