vue.js自定義指令詳解

寫(xiě)在文本前:相信在做vue的項(xiàng)目,你肯定接觸了指令,我們常用vue內(nèi)置的一些指令,比如v-model,v-text,v-if,v-show等等,但是這些內(nèi)置指令不在本文的講解范疇,本文想說(shuō)的是其自定義指令。思考一下,自定義指令我們用在哪里?是不是一下子想不出來(lái),感覺(jué)做一般的項(xiàng)目很少用到指令呢,想不到指令用在哪?。∫?yàn)槲覀兊拇_很少用,但不是說(shuō)不會(huì)用??垂俜絘pi文檔里有這么一句話:普通 DOM 元素進(jìn)行底層操作,這時(shí)候就會(huì)用到自定義指令。也就是說(shuō)我們?cè)诓僮髌胀―OM的時(shí)候有可能會(huì)用到。在做項(xiàng)目的時(shí)候,我們有很多種方法來(lái)替代自定義指令,所以讓我們忽略了vue自定義指令的用處。下邊我們一步步來(lái)了解vue自定義指令。

舉幾個(gè)栗子

(1、輸入框焦點(diǎn)自動(dòng)觸發(fā)。
(1、輸入框的電話號(hào)碼,郵箱,身份證號(hào)碼等的校驗(yàn)。

上面這幾個(gè)場(chǎng)合,我們?cè)谧鲰?xiàng)目的時(shí)候完全可以用其他方法代替,但是vue自定義指令能讓我們做到一勞永逸,做到一處定義,全局調(diào)用。使其代碼簡(jiǎn)潔高效,維護(hù)方便。接下來(lái),我們會(huì)一步步深入vue自定義指令。

1、首先看下官方介紹,如下:

/*  自定義指 */
import Vue from 'vue'
/**
 * 模板
 * v-lang
 * 五個(gè)注冊(cè)指令的鉤子函數(shù)
 */
Vue.directive('mydirective', {
  /**
   * 1.被綁定
   * 做綁定的準(zhǔn)備工作
   * 比如添加事件監(jiān)聽(tīng)器,或是其他只需要執(zhí)行一次的復(fù)雜操作
   */
  bind: function(el, binding, vnode) {
    console.log('1 - bind');
  },
  // 2.綁定到節(jié)點(diǎn)
  inserted: function(el, binding, vnode) {
    console.log('2 - inserted');
  },
  /**
   * 3.組件更新
   * 根據(jù)獲得的新值執(zhí)行對(duì)應(yīng)的更新
   * 對(duì)于初始值也會(huì)調(diào)用一次
   */
  update: function(el, binding, vnode, oldVnode) {
    console.log('3 - update');
  },
  // 4.組件更新完成
  componentUpdated: function(el, binding, vnode, oldVnode) {
    console.log('4 - componentUpdated');
  },
  /**
   * 5.解綁
   * 做清理操作
   * 比如移除bind時(shí)綁定的事件監(jiān)聽(tīng)器
   */
  unbind: function(el, binding, vnode) {
    console.log('5 - bind');
  }
})
/**
鉤子函數(shù)
1、bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用,用這個(gè)鉤子函數(shù)可以定義一個(gè)綁定時(shí)執(zhí)行一次的初始化動(dòng)作。
2、inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用(父節(jié)點(diǎn)存在即可調(diào)用,不必存在于document中)。
3、update:被綁定于元素所在的模板更新時(shí)調(diào)用,而無(wú)論綁定值是否變化。通過(guò)比較更新前后的綁定值,可以忽略不必要的模板更新。
4、componentUpdated:被綁定元素所在模板完成一次更新周期時(shí)調(diào)用。
5、unbind:只調(diào)用一次,指令與元素解綁時(shí)調(diào)用。
*/

執(zhí)行順序:
頁(yè)面加載時(shí)

bind
inserted

組件更新時(shí)

update
componentUpdated

卸載組件時(shí)

unbind

官方地址請(qǐng)移步至,directive,上面只是做了個(gè)簡(jiǎn)單的說(shuō)明,接下來(lái)我們?cè)敿?xì)的介紹vue自定義指令的各個(gè)鉤子函數(shù)到底如何觸發(fā)。

2、鉤子函數(shù)簡(jiǎn)單講解

就個(gè)人而言, bind和update也許是這五個(gè)里面最有用的兩個(gè)鉤子了.這個(gè)也是我們下邊主要要說(shuō)的2個(gè)鉤子函數(shù)。其它函數(shù)不常用,我也沒(méi)搞明白。

(1)鉤子函數(shù) bind

bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。

<input id="input1" v-mydirective> // html元素

Vue.directive('mydirective', {
  bind: function(el, binding, vnode) {
   el.style.border = "1px solid red"
  }
}

上面代碼的效果就是輸入框的邊框變成了1像素的紅色。當(dāng)然你也可以設(shè)置其它試試。

<input id="input1" v-mydirective> // html元素

Vue.directive('mydirective', {
  bind: function(el, binding, vnode) {
    el.addEventListener('click', function (event) {
        alert('hellow world')
    })
  }
}

在bind鉤子函數(shù)里,我們可以添加一些事件,在其某種條件下觸發(fā)(比如點(diǎn)擊事件,鍵盤(pán)事件),如上代碼,點(diǎn)擊input輸入框就會(huì)彈出 “hellow world”。至于bind里面的參數(shù)的含義,建議自己動(dòng)手輸入出來(lái)看看。很簡(jiǎn)單是吧,下邊我們來(lái)些稍微復(fù)雜一點(diǎn)的。看如下代碼:

指令鉤子函數(shù)會(huì)被傳入以下參數(shù):

/**
鉤子函數(shù)的參數(shù):(el, binding, vnode, oldVnode)
el:指令所綁定的元素,可以用來(lái)直接操作 DOM 。
binding:一個(gè)對(duì)象,包含以下屬性
  name:指令名,不包含v-的前綴;
  value:指令的綁定值;例如:v-my-directive="1+1",value的值是2;
  oldValue:指令綁定的前一個(gè)值,僅在update和componentUpdated鉤子函數(shù)中可用,無(wú)論值是否改變都可用;
  expression:綁定值的字符串形式;例如:v-my-directive="1+1",expression的值是'1+1';
  arg:傳給指令的參數(shù);例如:v-my-directive:foo,arg的值為 'foo';
  modifiers:一個(gè)包含修飾符的對(duì)象;例如:v-my-directive.a.b,modifiers的值為{'a':true,'b':true}
vnode:Vue編譯的生成虛擬節(jié)點(diǎn);
oldVnode:上一次的虛擬節(jié)點(diǎn),僅在update和componentUpdated鉤子函數(shù)中可用。
*/

這里我們著重注意一下,binding對(duì)象下的value,oldValue,arg,expression,modifiers這幾個(gè)屬性,根據(jù)這幾個(gè)屬性,我們可以做到更強(qiáng)大的自定義指令,下邊我們參考案例來(lái)說(shuō):

<div v-mydirective:left="100" id=”box"></div> // html元素

Vue.directive('mydirective', {
  bind: function(el, binding, vnode) {
       el.style.position = 'fixed';
       const s = (binding.arg === 'left' ? 'left' : 'top');
       el.style[s] = binding.value + 'px';
  }
}

上面的代碼是使id=”box“的這個(gè)div元素絕對(duì)定位了,而且設(shè)置了left的值為100px;

(2)鉤子函數(shù) update

update:每當(dāng)元素本身更新(但是子元素還未更新)時(shí)觸發(fā)。這句話怎么理解呢?我們用案例來(lái)說(shuō)明,如下:

<template>
  <div id="box">
       <input v-model="myname" v-mydirective/>
  </div>
<template>
export default {
  data(){
    return{
      myname:"zhangshan"
    }
  },
  directives:{
    'mydirective':{
         update: function(el, binding, vnode, oldVnode) {
              console.log('update更新了');
         }
    }
  }
}

上邊代碼,輸入框默認(rèn)值是"zhangshan",如果我們修改了myname值,那么就會(huì)觸發(fā)update函數(shù)的執(zhí)行,輸出“update更新了”

再來(lái)個(gè)復(fù)雜一點(diǎn)的案例如下:

<template>
  <div id="box">
       <input type="text" v-model="myname"  v-mydirective="{type:'name',val:myname}"/>
       <input type="text" v-model="myemail"  v-mydirective="{type:'email',val:myemail}"/>
  </div>
<template>
export default {
  data(){
    return{
      myname:"zhangshan",
      myemail:'***@163.com'
    }
  },
  directives:{
    'mydirective':{
         update: function(el, binding, vnode, oldVnode) {
           //update:指令的值可能發(fā)生了改變,也可能沒(méi)有。但是你可以通過(guò)比較更新前后的值來(lái)忽略不必要的模板更新 
           if(binding.oldValue.val != binding.value.val){// 進(jìn)行判斷,避免同時(shí)觸發(fā)多個(gè)判斷
             let checkStatus = true;// 定義驗(yàn)證狀態(tài) 局部變量
             if(binding.value.type == 'name'){
                   if(binding.value.val.length>5){
                        checkStatus = false;
                        console.log("輸入的姓名長(zhǎng)度大于5了")
                   }
             }
             if(binding.value.type == 'email'){// 驗(yàn)證郵箱
               if(!/^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-   z0-9]+$/g.test(binding.value.val)){
                  checkStatus = false;
               }
             }
             if(checkStatus){
                el.style.border = "1px solid #333";//驗(yàn)證通過(guò)input邊框顏色就變成#333
             }else{
                el.style.border = "1px solid red";//驗(yàn)證不通過(guò)input邊框顏色就變成紅色
             }
         }
       }
    }
  }
}

上邊的案例,我們通過(guò)bind下的modifiers屬性實(shí)現(xiàn)。modifiers:一個(gè)包含修飾符的對(duì)象;例如:v-my-directive.a.b,modifiers的值為{'a':true,'b':true}。這樣我們?cè)趗pdate鉤子函數(shù)里根據(jù)所傳入的type類型做判斷來(lái)執(zhí)行哪個(gè)驗(yàn)證方法。

(3)鉤子函數(shù) inserted

inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用(父節(jié)點(diǎn)存在即可調(diào)用,不必存在于document中)。

<input id="input1" v-mydirective> // html元素

Vue.directive('mydirective', {
  inserted: function(el, binding, vnode) {
    el.focus()//輸入框自動(dòng)獲取焦點(diǎn)
  }
}

對(duì)于鉤子函數(shù) inserted,我也是一知半解,所用的不多,但是有個(gè)常用的方法是寫(xiě)在這里面的,就是輸入框自動(dòng)獲取焦點(diǎn),也就是上面的代碼。

(4)鉤子函數(shù) bind和鉤子函數(shù) inserted的區(qū)別。
據(jù)文檔所說(shuō),插入父節(jié)點(diǎn)時(shí)調(diào)用 inserted,來(lái)個(gè)測(cè)試。
bind: function (el) {
 console.log(el.parentNode) // null
 console.log('bind')
},
inserted: function (el) {
 console.log(el.parentNode) // <div id="login">...</div>這個(gè)元素是指令所在元素的父元素,具體根據(jù)自己的寫(xiě)法不同
 console.log('inserted')
}

分別在兩個(gè)鉤子函數(shù)中輸出父節(jié)點(diǎn):bind 時(shí)父節(jié)點(diǎn)為 null,inserted 時(shí)父節(jié)點(diǎn)存在。

(5)鉤子函數(shù) componentUpdated和鉤子函數(shù) unbind
 // 4.組件更新完成
  componentUpdated: function(el, binding, vnode, oldVnode) {
        console.log('4 - componentUpdated')
  },
   /*** 5.解綁
     * 做清理操作
     * 比如移除bind時(shí)綁定的事件監(jiān)聽(tīng)器
   */
    unbind: function(el, binding, vnode) {
       console.log('5 - bind');
    }

對(duì)于這2個(gè)鉤子函數(shù),沒(méi)有做太多的研究,unbind可能會(huì)用到,用于釋放實(shí)例資源占用等操作。

關(guān)于vue.js指令大致也就說(shuō)這么多吧,如果錯(cuò)誤,歡迎指正拍磚......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容