vue自定义指令两种方式,vue3.0自定义指令
除了默认的核心指令(v-model和v-show),Vue还允许注册自定义指令。下面文章主要介绍如何在Vue3中实现自定义指令的相关信息。用示例代码介绍的很详细,有需要的朋友可以参考一下。
目录
生命周期钩子的序参数简化形式对象字面量关于组件的使用说明几个实用的自定义指令自动对焦v对焦防抖v去抖节流v节流弹窗隐藏v隐藏摘要在开发Vue项目的时候,大部分人都会用到一些Vue内置的指令,比如v-model,v-if等。在使用它们的时候,我不知道自己有没有想过实现一个指令。本文以Vue3项目为例,从原理和方法到实际案例和注意事项,阐述了如何尽可能细致地定制说明书。
前言
我们需要理解为什么我们需要定制一个指令。其实我们是想更简洁地复用操作DOM的逻辑,类似于组件化、组合化的功能。
无论是Vue的内置指令,还是自定义指令,都有类似于组件的生命周期。我们可以在不同的生命周期中完成不同的逻辑操作,并将其绑定到组件元素,从而创建一个自定义指令。在Vue3中,我们可以用三种方式定义指令:
如果是脚本设置定义组件内的指令,有一个语法糖可以使用:任何以V开头的hump style命名的变量都可以作为自定义指令,然后在模板中使用。举个简单的例子:输入框渲染后自动聚焦。
脚本设置
//在模板中启用v焦点
const vFocus={
mounted: (el)=el.focus()
}
/脚本
模板
输入垂直焦点/
/模板
操作效果:
如果使用选项,则需要在指令选项中注册自定义指令。与上一个示例相同:
脚本
导出默认值{
setup() {},
指令:{
//指令名
焦点:{
//生命周期
已安装(el) {
//处理DOM的逻辑
El . focus();
},
}
}
}
/脚本
模板
输入垂直焦点/
/模板
实现效果与上例相同。
除了在组件中注册指令之外,我们还可以定制全局指令,以便它可以在所有组件中使用。
//main.js
从“vue”导入{ createApp }
从导入应用程序。/App.vue
const app=createApp(App)
app.directive(focus ,{
已安装(el) {
El . focus();
}
})
app . mount(# app)
实现效果是一样的。
我们选择三种方式中的最后一种,另外两种方式可以用类似的方式实现。
生命周期
指令的生命周期类似于组件的生命周期:
app.directive(focus ,{
已创建(){
console . log( created );
},
beforeMount() {
console . log( before mount );
},
已安装(){
console.log(mounted )。
},
更新之前(){
console.log(beforeUpdate )。
},
已更新(){
console.log(“已更新”);
},
卸载之前(){
console . log( before unmount );
},
卸载(){
console . log( unmounted );
}
})
运行结果:
请注意,该指令没有beforeCreated挂钩。
Created:在绑定元素的属性之前调用beforeMount,或者在应用事件侦听器之前:在元素插入DOM之前调用。比如我们要实现输入框的自动聚焦,就不能在beforeMount hook中实现mounted:在绑定元素的父组件和它自己的所有子节点挂载之后调用。此时,DOM已经渲染完毕。我们还在这个钩子函数中实现了输入框的自动聚焦:在绑定元素的父组件更新之前调用beforeUpdatebeforeUnmount在绑定元素的父组件及其所有子组件更新后调用;在卸载绑定元素的父组件之前调用unmounted绑定元素的父组件卸载后,每个钩子函数都有相应的参数。接下来,看看钩子参数。
钩子的参数
指令是为了复用DOM的运算逻辑,所以指令参数可以有1-4个参数,其中需要的参数是当前绑定的DOM元素。
语法:
已创建(el,binding,vnode,preVnode) {}
参数很多,我们一个一个学吧。
El:指令绑定的DOM元素可以用来直接操作当前元素。默认情况下,el参数被传递到钩子中。比如我们开始实现的focus指令就是直接操作的元素DOM。
绑定:这是一个包含以下属性的对象:
Value:在元素上使用指令时传递给指令的值。比如:div v-reverse=hello/div,传递给reserve指令的值是hello,我们就可以得到该值并做相关处理。oldValue:之前的值一般用在beforeupdate和updated hook函数中,例如:beforeUpdate(el,{oldValue: })arg:传递给指令的参数,可选,如div v-reverse:foo=hello/div,那么传递给指令的参数是foomodifiers:由修饰符组成的对象,如div v-reverse . foo . bar= hello /div,实例:使用本指令的组件实例。注意不是DOMdir:指令的定义对象vnode:绑定元素的本地VNode。
PreVnode:表示上一次渲染中指令所绑定的元素的Vnode,一般用在beforeUpate和updated钩子函数中。
看这些参数可能会比较混乱。让我们看一个例子:
定义一个可逆的输入框输入指令。注意钩子函数应该选择beforeUpdate。
app.directive(保留,{
更新前(el,binding) {
console.log(绑定);
el.innerText=binding.value?binding.value.split(“”)。反转()。联接(“”):“”;
}
})
在模板中,使用:输入框输入一个值,div将显示取反后的值。
脚本设置
从“vue”导入{ref}
let hello=ref( )
/脚本
模板
输入v-focus v-model=hello /
div v-reserve:foo . bar= hello /div
/模板
运行结果:
如果结合这个图,可以更好的理解钩子参数的意义。
简化形式
当我们写指令时,我们可以指定在哪个钩子中执行一些逻辑。有时当一个指令有多个钩子,但它是一个重复的逻辑操作时,重新编写代码显然是不优雅的。在Vue中,如果我们在定制指令时需要在mounted和updated中实现相同的行为,并且不关心其他钩子,那么我们可以采用简写:
app.directive(color ,(el,binding)={
//这将在挂载和更新时被调用
El . style . color=binding . value;
})
对象字面量
在我们前面的例子中,只有一个值被传递给指令。如果要给指令传入多个值,应该怎么做?很简单。您可以只传入一个文本对象,它可以直接在模板中声明,或者您可以使用一个响应对象。使用时,binding.value是一个对象,而不是普通的值。
脚本设置
从“vue”导入{ref,reactive}
let hello=ref( )
const obj=reactive({
你好: ,
世界:“”
})
/脚本
模板
输入v-focus v-model=obj.hello /
div v-reserve:foo.bar=obj/div
!-div v-reserve:foo . bar= { hello:obj . hello,world: obj.world}/div -
/模板
相应地,我们的说明应该稍加修改:
el.innerText=binding.value?binding.value.hello.split( )。反转()。联接(“”):“”;
达到的效果还是和上面一致的。
在组件上使用指令
直接在元素上使用指令,我们可以在指令中操作DOM,这已经不是问题了。如果在组件上使用指令会发生什么?组件实际上封装了一些DOM元素。与Vue3和Vue2不同,Vue3的模板中可以有多个根节点。
让我们创建一个新的Reverse.vue,作为组件在后面介绍。
Vue2:模板中只能有一个根节点,所以会报错。
//Reverse.vue
模板
分区/分区
分区/分区
/模板
Vue3:模板中可以有多个根节点,这很正常。
//Reverse.vue
模板
分区/分区
分区/分区
/模板
因为该指令用于操作DOM元素,所以如果只有一个根节点,就不会有问题,例如:
脚本设置
.
从导入反向值。/Reserve.vue
.
/脚本
模板
.
ReverseVue v-reserve=obj/
/模板
//Reverse.vue
模板
!-此处将应用v - v-reserve指令-
分区/分区
/模板
如果模板中有多个根节点,将会抛出警告,并且不会执行指令。
//Reverse.vue
模板
!-v-reserve不起作用,会抛出警告-
分区/分区
分区/分区
/模板
结论:尽量不要在组件上使用自定义指令,除非确定只有一个根节点。
几个实用的自定义指令
下面的指令都是全局指令。
自动聚焦v-focus
Focus比较特殊,兄弟元素之间只有一个焦点,也就是说,如果这个命令应用于两个兄弟输入框,只有一个会被自动聚焦。
app.directive(focus ,(el)={
El . focus();
})
防抖v-debounce
在实际项目开发中,我们经常听到服务器上的同事抱怨:为什么前端不限制电流?前端“限流”通常采用防抖和节流。我们先来看看如何实现防抖。
步骤:
首先我们要知道如何写一个防抖函数,然后我们需要将防抖函数绑定到el节点上。为了具有普遍性,我们还需要考虑传入事件的类型。最后,我们需要卸载定时器和其他操作。App。方向性(去抖,{
已安装(el,装订){
//至少需要回调函数和监听事件类型。
if (typeof binding.value.fn!==函数 !binding.value.event)返回;
设延迟=200;//默认延迟时间
el.timer=null
el.handler=function() {
if (el.timer) {
clear time out(El . timer);
el.timer=null
};
el.timer=setTimeout(()={
binding.value.fn.apply(this,参数)
el.timer=null
},binding . value . delay delay);
}
El . addevent listener(binding . value . event,el.handler)
},
//记得在卸载元素之前清除计时器并移除侦听事件。
安装前(el,binding) {
if (el.timer) {
clear time out(El . timer);
el.timer=null
}
El . removeeventlistener(binding . value . event,el.handler)
}
})
在模板中使用:
脚本设置
const handleClick=()={
Console.log(防抖点击);
}
/脚本
模板
button v-de bounce= { fn:handle Click,event: click ,delay: 200} 点击尝试/button
/模板
运行结果:
单击快速按钮不会立即触发手柄单击,而是在指定的延迟时间后才会触发。
节流v-throttle
类似节流防抖,用于前端“限流”。不同的是,防抖是为了限制执行次数。多次密集触发只会执行最后一次,不规则,更注重结果;节流就是限制执行频率,有节奏的执行,规律性,更加注重过程。
节流的实现类似于防抖:
app.directive(节流,{
已安装(el,装订){
//至少需要回调函数和监听事件类型。
if (typeof binding.value.fn!==函数 !binding.value.event)返回;
设延迟=200;
el.timer=null
el.handler=function() {
if (el.timer)返回;
el.timer=setTimeout(()={
binding.value.fn.apply(this,参数)
el.timer=null
},binding . value . delay delay);
}
El . addevent listener(binding . value . event,el.handler)
},
//记得在卸载元素之前清除计时器并移除侦听事件。
安装前(el,binding) {
if (el.timer) {
clear time out(El . timer);
el.timer=null
}
El . removeeventlistener(binding . value . event,el.handler)
}
})
在模板中使用:
脚本设置
从“vue”导入{reactive}
const obj=reactive({
你好: ,
世界:“”
})
const handleInput=()={
Console.log(节流输入框值:,obj . hello);
}
/脚本
模板
输入v-throttle={fn: handleInput,event: input ,delay:1000 } v-model= obj . hello /
/模板
运行结果:
HandleInput不是由我在输入框输入多快触发的,而是在固定的时间间隔内触发一次,这就是节流。
弹窗隐藏v-hide
在实际开发中,会有这样的需求:点击一个按钮出现一个弹窗,然后点击弹窗的其他区域关闭弹窗。如果是被点击的弹窗本身,弹窗除非关闭,否则不会关闭。
为了达到这种效果,大多数人会想到对点击事件进行全局监控,判断被点击的目标元素是否与我们的弹出元素相同,如果不相同,那么隐藏弹出。那么我们来看看应该如何实现:
app.directive(hide ,{
已安装(el,装订){
el.handler=函数(e) {
//如果点击范围在绑定元素范围内,则执行原来的点击事件,而不是指令操作。
if (el.contains(e.target))返回;
if(type of binding . value . fn=== function ){
//如果绑定到指令的是一个函数,它将回调并指定这个
binding.value.fn.apply(this,参数)
//不建议使用style隐藏元素,这样控制弹出窗口的变量就无法更改,建议使用回调函数。
//El . style . display= none ;
//解除事件绑定
document . removeeventlistener( click ,el.handler)
}
}
//监控全局点击事件
document . addevent listener( click ,el.handler)
//如果同步绑定全局事件没有生效,可以异步完成。
//setTimeout(()={
//document . addevent listener( click ,el.handler)
//}, 0);
},
//解除事件绑定
安装前(el) {
document . removeeventlistener( click ,el.handler)
}
})
在模板中使用:
脚本设置
从“vue”导入{ref}
设isShowModal=ref(false)
const showModal=()={
isShowModal.value=true
}
const cancleModal=()={
console . log( cancleModal );
isShowModal.value=false
}
/脚本
模板
Button @click.stop=showModal 单击以显示弹出窗口/按钮
div class= modal v-hide= { fn:cancleModal } v-if= isShowModal
p我是弹出窗口/p
Button @click.stop=取消模式关闭/button
/div
/模板
运行结果:
总结
在本文中,我们试图用一种通俗易懂的方式来完整地梳理Vue3中如何定制指令。合理使用指令可以帮助我们更快地解决问题。值得注意的是:
在选择指令的钩子函数时,要明确不同的钩子函数有不同的作用。您应该卸载全局变量、计时器、事件绑定等。由钩子函数及时定义。避免影响其他组件使用,以及内存泄漏如果涉及到DOM操作,就要想到是否有可能尽快把指令拔出来。本文介绍了如何在Vue3中实现自定义指令。请搜索我们以前的文章或继续浏览下面的相关文章以获取更多信息。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。