0


Vue自定义指令

文章目录


1. 概述

除了核心功能默认内置的指令,Vue也允许注册自定义指令。有的情况下,对普通 DOM 元素进行底层操作,这时候就会用到自定义指令绑定到元素上执行相关操作。

自定义指令分为:

全局指令和局部指令,当全局指令和局部指令同名时以局部指令为准。

局部指令:只对当前实例(或组件)生效

全局指令:对全部实例(或组件)都生效

2. 钩子函数

自定义指令常用钩子函数:

  1. bind 第一次绑定到元素时调用(初始化)
  2. inserted 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
  3. update 数据更新时调用
  4. componentUpdated 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  5. unbind 只调用一次,指令与元素解绑时调用。

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。
  • binding:一个对象,包含以下属性。 - value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。- oldValue:之前的值,仅在 beforeUpdateupdated 中可用。无论值是否更改,它都可用。- arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。- modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。- instance:使用该指令的组件实例。- dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。
  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdateupdated 钩子中可用。

钩子函数可以理解为一个类,类中的构造函数绑定了5个函数(即钩子函数),当我们自定义钩子函数时,就会初始化这个类,然后让我们的相关代码按顺序执行这5个函数。

有关钩子函数更详细的解释,下面这篇文章中的前两个教程写得很通俗易懂,特此推荐:

https://blog.csdn.net/weixin_39345003/article/details/80813765

钩子函数的执行顺序:

<divid="app1"><divv-redv-if="isShow"><inputtype="text"v-model="title"></div></div><script>
    Vue.directive('red',{// bind 第一次绑定到元素时调用bind(el, bindings){
            console.log('bind');},// insertedinserted(el, bindings){
            console.log('inserted');},// updateupdate(el, bindings){
            console.log('update');},// componentUpdatecomponentUpdated(el, bindings){
            console.log('componentUpdated');},// unbindunbind(el, bindings){
            console.log('unbind');},})const vm1 =newVue({el:'#app1',data:{isShow:true,title:'钩子函数执行顺序'}})</script>

程序一执行,当数据源中的数据第一次绑定了元素就会执行

bind

函数,当绑定元素插入到父元素中,即显示到视图中时,会执行

inserted

函数:

在这里插入图片描述

当我们改变视图,使得数据发生改变时,就会执行

update

componentUpdated

函数:

在这里插入图片描述

当我们销毁被绑定元素时,即被绑定元素和数据源解绑,就会触发

unbind

函数:

在这里插入图片描述

在这里插入图片描述

3. 自定义全局指令

描述:

全局定义的指令,所有的组件或vue的实例都会生效。

语法:

Vue.directive('指令名称,不需要写v-开头',对象或函数)
Vue.directive('test',{
    bind(el,bind){
        console.log(el)
    }
})

案例:

<divid="app1"><divv-red>{{title}}</div></div><divid="app2"><divv-red>标题2</div></div><script>//自定义全局指令
    Vue.directive('red',{// bind 第一次绑定到元素时调用bind(el, bindings){
            el.style.cssText =`color:red;font-size:30px`}})const vm1 =newVue({el:'#app1',data:{isShow:true,title:'标题1'}})const vm2 =newVue({el:'#app2',data:{}})</script>

在这里插入图片描述

4. 自定义局部指令

描述:

定义局部指令,只有当前的实例能用。

语法:

new Vue({
    directives: {
        test:{
            bind(el,bind){}
        },
        // bind/update
        test2(el,bind){}
    }
})

案例:

<divid="app1"><divv-red>{{title}}</div></div><divid="app2"><divv-red>标题2</div></div><script>const vm1 =newVue({el:'#app1',data:{isShow:true,title:'标题1'},// 定义局部指令,只有当前的实例能用directives:{red:{// bind它还没有绑定到父元素中,初始化bind(el){
                    el.style.cssText =`color:red;font-size:30px`}}}})const vm2 =newVue({el:'#app2',data:{},directives:{red:{bind(el){
                    el.style.cssText =`color:blue;font-size:30px`}}}})</script>

在这里插入图片描述

利用自定义局部指令操作Dom:

<divid="app1"><divv-red>{{title}}</div></div><divid="app2"><divv-red>标题2</div></div><script>const vm1 =newVue({el:'#app1',data:{isShow:true,title:'标题1'},directives:{red:{// bind表示被绑定元素还没有插入到父元素中bind(el){
                    el.style.cssText =`color:red;font-size:30px`const divDom = document.createElement('div')
                    divDom.innerHTML ='我是标题1的孩子'
                    el.appendChild(divDom)},// 这时被绑定元素已经插入到父元素中去了,所以可以打印出被绑定元元素的父节点inserted(el){
                    console.log(el.parentNode);}}}})const vm2 =newVue({el:'#app2',data:{},directives:{red:{bind(el){
                    el.style.cssText =`color:blue;font-size:30px`}}}})</script>

在这里插入图片描述

注意:在上面的代码中,我们在

bind

函数中,不能获取当前被绑定元素的父节点,因为此时被绑定元素刚刚初始化,还没有插入到父节点当中。在

inserted

函数中才能获取被绑定元素的父节点,因为此时元素已经插入到父节点当中去了。

5. 使用自定义指令实现权限管理

目标:

根据地址栏中的数据,决定是否显示 button 按钮。如果地址栏中的 username 的值是 admin 时,就显示 button 按钮,否则不显示。

注意:目标中的显示与不显示,是取决于该 Dom 元素(button 按钮)是否存在,而不是通过 css 来进行显示与隐藏。

代码:

<divid="app1"><buttonv-auth>查看工资</button></div><script>const vm1 =newVue({el:'#app1',data:{isShow:true,title:'标题1'},directives:{auth:{// 注意:是否删除按钮的操作,必须在被绑定元素(即当前元素)已经插入父节点(插入到视图)当中后进行inserted(el){// 这是一种比较粗暴的写法,可维护性较低// if (location.search != '?username=admin') {//   el.remove()// }// URLSearchParams它是html5提供的新的Api方法,用于获取url地址中的search转为对象let urlSearch =newURLSearchParams(location.search)// 这样的写法可维护性较高,假如显示元素的权限还需要给到 张三 用户,则直接修改判断条件中的表达式即可if(urlSearch.get('username')!='admin'){// 以前兼容性更好的写法,但是现在可以不管// el.parentNode.removeChild(el)
                        el.remove()}}}}})</script>

在这里插入图片描述

在这里插入图片描述

6. 使用自定义指令实现表单验证

首先我们先完成验证手机号的功能。

目标:

在初始化(在 bind 函数中进行)和更新数据(在 update 函数中进行)时都要进行手机号的验证。

思路:

先获取手机号(收集数据),再用正则表达式判断输入框中的手机号是否合法,如果合法则手机号显示黑色,如果不合法则手机号显示红色。

代码:

<divid="app"><div><inputtype="text"v-phone="phone"v-model="phone"></div></div><script>  
    Vue.directive('phone',{// 方法1:直接通过dom来完成数据的收集// update(el) {//   console.log(el.value);// }// 方法2:可以通过传值的方式(钩子函数)完成数据收集bind(el,{ value }){// 手机号码let reg =/^1[3-9]\d{9}$/if(!reg.test(value)){// 不合法
                el.style.color ='red'}else{
                el.style.color ='black'}},update(el,{ value }){// 手机号码let reg =/^1[3-9]\d{9}$/if(!reg.test(value)){// 不合法
                el.style.color ='red'}else{
                el.style.color ='black'}}})const vm1 =newVue({el:'#app',data:{phone:'13525125121',}})</script>

在这里插入图片描述

在上面代码中,通过钩子函数完成数据获取与验证,代码重复率高,所以在钩子函数部分,我们可以简写成下面这种方式:

Vue.directive('phone', (el, { value }) => {
    // 手机号码
    let reg = /^1[3-9]\d{9}$/
    if (!reg.test(phone)) {
        // 不合法
        el.style.color = 'red'
    } else {
        el.style.color = 'black'
    }
}) 

注意:简写方式就是自定义指令语法中,第二个参数是函数的写法。自定义指令的简写,指的是将 bind 函数和 update 函数封装起来的写法。

在上面的基础上,我们再加上验证错误信息的显示:

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>vue学习使用</title><scriptsrc="./js/vue.js"></script></head><body><divid="app"><div><inputtype="text"v-phone="phone"v-model="phone"></div></div><script>
            Vue.directive('phone',(el,{ value })=>{// 手机号码let reg =/^1[3-9]\d{9}$/if(!reg.test(value)){// 不合法
                    el.style.color ='red'// 没有 span 标签时,就创建 span 标签if(!el.nextSibling){const spanDom = document.createElement('span')
                        spanDom.innerHTML ='不合法,修改一下'// 这里是防止初始化时数据就不合法,导致被绑定元素不能成功插入到父结点中// el.parentNode?.appendChild(spanDom)// 上面的写法要求的浏览器版本较高,下面的写法兼容性更好
                        el.parentNode && el.parentNode.appendChild(spanDom)}}else{// 输入正确时,移除 span 标签
                    el.style.color ='black'
                    el.nextSibling && el.nextSibling.remove()}})const vm1 =newVue({el:'#app',data:{phone:'13525125121',phoneMsg:''}})</script></body></html>

在这里插入图片描述

最后,我们使用模块化的的思路,将案例完善一下:

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>vue学习使用</title><scriptsrc="./js/vue.js"></script></head><body><divid="app"><div><!-- 这种写法的 value 值直接就是 phone --><!-- <input type="text" v-validate="phone" v-model="phone"> --><inputtype="text"v-validate.phonev-model="phone"></div><div><inputtype="text"v-validate.emailv-model="email"></div><div><!-- "{len:3,msg:'长度过长'}":v-validate指令的value值 --><inputtype="text"v-validate.str="{len:3,msg:'长度过长'}"v-model="str"></div></div><script>// 在对象里存方法const validateMethod ={phone(el){// 手机号码let reg =/^1[3-9]\d{9}$/if(!reg.test(el.value)){// 不合法
                        el.style.color ='red'if(!el.nextSibling){const spanDom = document.createElement('span')
                            spanDom.innerHTML ='不合法,修改一下'// el.parentNode?.appendChild(spanDom)
                            el.parentNode && el.parentNode.appendChild(spanDom)}}else{
                        el.style.color ='black'
                        el.nextSibling && el.nextSibling.remove()}
                    console.log('phone');},email(el, value){
                    console.log('email')},str(el, value){// 这里这个判断是容错处理,也可以不写。因为我们上面的代码中给 value传值了// 如果当前封装好的代码(当作组件)给别人使用的话,使用者可能不穿值if(value){if(el.value.length > value.len){if(!el.nextSibling){const spanDom = document.createElement('span')
                                spanDom.innerHTML = value.msg
                                // 防止初始化数据不合法,父节点不存在
                                el.parentNode?.appendChild(spanDom)}}else{
                            el.nextSibling?.remove()}}}}

            Vue.directive('validate',(el,{ value, modifiers })=>{// modifiers:一个包含修饰符的对象 (如果有的话)。// console.log(Object.keys(modifiers))===phone
                Object.keys(modifiers).forEach(name=>{// 调用对象中的方法
                    validateMethod[name](el, value)})})const vm1 =newVue({el:'#app',data:{phone:'13525125121',email:'[email protected]',str:'aaa'},methods:{}})</script></body></html>

在这里插入图片描述

注意:

v-validate="phone"

的 value 值是数据源中的 phone,

v-validate.phone

的 value 值是 undefined,

v-validate.str="{len:3,msg:'长度过长'}"

的 value 值是

{len:3,msg:'长度过长'}


本文转载自: https://blog.csdn.net/weixin_45605541/article/details/126693166
版权归原作者 月光晒了很凉快 所有, 如有侵权,请联系我们删除。

“Vue自定义指令”的评论:

还没有评论