Vue3 - 自定义指令封装
- 一. 自定义指令封装
- 1.1 全局/局部注册自定义聚焦指令
- 1.2 自定义指令相关参数
- 1.3 自定义指令参数传递
- 二. 总结
一. 自定义指令封装
vue
中有很多内置的指令,我们一般在开发中也经常用到,比如v-if,v-for
等等。那么本篇文章就来讲一讲如何自定义指令。
我们先来看下一个简单的功能,我们画一个简单的页面,然后页面里面有一个文本框:
<template>
<div class="border">
<h1 >我是父组件</h1>
文本框:<inpu type="text">
</div>
</template>
<script setup>
</script>
<style scoped>
.border {
border: 1px solid;
width: 400px;
height: 200px;
}
</style>
运行结果如下:
那么如果我希望页面刚进入,就能聚焦到这个文本框中,该怎么做?我们这里就使用自定义指令的功能来完成。
1.1 全局/局部注册自定义聚焦指令
首先我们说下局部的定义方式,我们在首页中添加以下代码:
<script setup>
const vFocus = {
mounted: (el) => {
console.log('mounted');
el.focus();
},
};
</script>
然后再inpu
标签上添加指令:<input v-focus type="text" />
,那么页面刷新一下,效果如下:可见已经聚焦到文本框中,并且输出了对应的内容。
紧接着再来说下全局的注册,主要在main.ts
入口文件中使用directive
函数进行注册。这样任何一个组件中都可以使用指令:v-focus
。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App)
.directive('focus', {
// 当被绑定的元素挂载到 DOM 中时
mounted(el) {
// 聚焦元素
el.focus()
}
})
.mount('#app')
讲到这里,其实我并没有对指令的定义做出太多的解释,先把这个功能代码贴出来,接下来开始细讲。
1.2 自定义指令相关参数
大家可以从案例中看到,在定义自定义指令的时候,我们定义了一个mounted
函数,其实除了他还有几个钩子函数。
created
:在绑定元素的attribute
前或事件监听器应用前调用。beforeMount
:当指令第一次绑定到元素上并且在父组件被挂载前调用。mounted
:在绑定元素的父组件及他自己的所有子节点都挂载完成后调用,一般我们自定义指令的逻辑都写在这里。beforeUpdate
:绑定元素的父组件及他自己的所有子节点都更新前调用。updated
:在绑定元素的父组件及他自己的所有子节点都更新后调用。beforeUnmount
:在父组件被卸载前调用。unmounted
:当指令和元素已经解除绑定并且父组件也已经被卸载的时候调用。
同样,我们还注意到,我们定义mounted
钩子函数的时候,还传入了一个参数el
,除此之外,还有三个参数,一共4个。
el
(读写):指令所绑定的元素,可以直接操作DOM
。binding
(只读):通过自定义指令传递的参数都在这里。vnode
(只读):Vue
编译生成的虚拟节点。oldVnode
(只读):上一个虚拟节点,仅仅在update
和componentUpdated
钩子函数中才可以使用。
1.3 自定义指令参数传递
我们来看下这个案例:
<template>
<div class="border">
<h2>我是父组件</h2>
<button v-onceClick:{name:myName,age:10}="1">按钮1秒</button>
<br>
<button v-onceClick:{name:myName,age:10}="3">按钮3秒</button>
</div>
</template>
<script setup>
const myName = "ljj";
const vOnceClick = {
mounted: (el, binding, vnode) => {
el.addEventListener("click", () => {
if (!el.disabled) {
el.disabled = true;
let time = binding.value * 1000;
setTimeout(() => {
el.disabled = false;
}, time);
}
console.log(binding.value,binding.arg)
});
console.log(vnode)
},
};
</script>
<style scoped>
.border {
border: 1px solid;
width: 400px;
height: 200px;
}
</style>
效果如下:
我们可以看到,首先打印的是Vue
编译后的虚拟节点相关信息。之后基本上同时点击了两个按钮。一个置灰了1秒,一个置灰了3秒。符合代码编写。
我们把这段代码拆分开来说:v-onceClick:{name:myName,age:10}="3"
- 冒号后面的是引入的值:
{name:myName,age:10}
这块部分,竟然是一个字符串,我们代码里面定义了myName变量,值是ljj。但是从输出结果来看,他并不是我们期望的。但是我们可以从binding.arg
中拿到这个值。 - 我们可以从
binding.value
中拿到="3"
这部分的数据。
后来经过我的测验,针对第一点,我们这样改,传入一个单对象,使用中括号:
<button v-onceClick:[{name:myName,age:10}]="1">按钮1秒</button>
<br />
<button v-onceClick:[myName]="3">按钮3秒</button>
结果如下:
二. 总结
总结下自定义指令的封装相关知识点:
- 自定义指令,我们一般需要定义一个对象,里面包含一个
mounted
函数,我们主要在这个函数中,对传入的el
参数,操作DOM
节点。编写自己的业务逻辑即可。 - 如果希望往指令函数中传入相关的参数,可以通过
v-指令名称:[Obj]=val
的写法来完成。 Obj
则由mounted
这样钩子函数的第二个参数binding
对象中获得,即biding.arg
。val
则通过biding.value
来获得。- 注意相关钩子函数中,一般我们只操作第一个参数
el
来控制DOM
节点,其他参数一般是只读的。 - 一般我们命名自定义指令
const v指令名称 = {}
,Vue3
写法中v
前缀一定要有,使用的时候则由v-指令名称
来指定。首字母大小写不影响。例如定义指令函数:vFocus
,那么使用的时候v-focus
或者v-Focus
都可。