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 = trueconst timer = setTimeout(() => {el.disabled = falseclearTimeout(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组件中可能同时存在多个根节点;
**
仅代表自己观点,如有错误及不合适地方,欢迎批评指正