1. 程式人生 > 程式設計 >詳解Vue自定義指令及使用

詳解Vue自定義指令及使用

一、什麼是指令

學習 vue 的時候肯定會接觸指令,那麼什麼是指令呢?

在 vue 中提供了一些對於頁面和資料更為方便的輸出,這些操作就叫做指令,以 v-xxx 表示,比如 html 頁面中的屬性 <div v-xxx ></div>

比如在 angular 中 以 ng-xxx 開頭的就叫做指令

指令中封裝了一些 DOM 行為,結合屬性作為一個暗號,暗號有對應的值,根據不同的值,會進行相關 DOM 操作的繫結,即可以進行一些模板的操作

vue 中常用的一些內建 v- 指令

  • v-text:元素的 innerText 屬性,只能用在雙標籤中, 和{{ }}效果是一樣的,使用較少
  • v-html:元素的 innerHTML,其實就是給元素的 innerHTML 賦值
  • v-show:元素的顯示與隱藏,基於 css 樣式的切換。如果確定要隱藏,會給元素的 style 加上display: none
  • v-if:元素的插入和移除操作,相當於對元素的銷燬和建立。如果表示式的值為 false,會留下一個 <!----> 作為標記,若未來 v-if 的值是 true 了,就在這裡插入元素(如果 if 有 else 就不要單獨留坑了)。
  • v-else-if:前一個相鄰元素必須有 v-if 或 v-else-if
  • v-else:前一個相鄰元素必須有 v-if 或 v-else-if,如果 v-if 和 v-else-if 都有對應的表示式,則 v-else 可以直接寫
  • v-for:用於迴圈渲染一組資料(陣列或物件)。必須使用特定語法:v-for="alias in expression"。注:當 v-for 和 v-if 同處於一個節點時,v-for 的優先順序比 v-if 更高。即 v-if 將執行在每個 v-for迴圈中
  • v-on:主要用來監聽 dom 時間,然後執行一些操作。簡寫為【@】
  • v-model:用於 input/textarea 等表單控制元件上建立雙向資料繫結。
  • v-bind:動態的繫結一個或多個屬性,常用於繫結 class,style,href 等。
  • v-once:元件和元素只渲染一次,當資料發生變化,也不會重新渲染。

v-if 和 v-show 的對比

  • v-if 是真正的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子元件適當的被銷燬和重建。
  • v-if 也是惰性的,如果在初始渲染時條件為假,則什麼也不做,直到條件第一次為真時,才會開始渲染條件塊。
  • 相比之下 v-show 就簡單的多,不管初始條件是什麼,元素總是會被渲染,並且只是簡單的基於 css 進行切換。

一般來說,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。因此,如果需要非常頻繁的切換,則使用 v-show 較好,如果在執行時條件很少改變,則使用 v-if 較好。

在實際的開發過程中,可能這些內建指令並不能滿足所有的需求,或者說想為元素附件一些特別的功能。這時候,就需要用到 Vue 給我們程式設計客棧提供的一個強大又靈活的功能「 自定義指令 」。

官方api 文件裡有這麼一句話:對普通 DOM 元素進行底層操作,這時候就會用到自定義指令。也就是說自定義指令解決的問題或者說使用場景是對普通 DOM 元素進行底層操作,所以我們不能盲目的胡亂的使用自定義指令。

二、自定義指令的鉤子函式

Vue 提供了自定義指令的5個鉤子函式:

  • bind:指令第一次繫結到元素時呼叫,只執行一次。在這裡可以進行一次性的初始化設定。
  • inserted:被繫結的元素,插入到父節點的 DOM 中時呼叫(僅保證父節點存在)。
  • update:元件更新時呼叫。
  • componentUpdated:元件與子元件更新時呼叫。
  • unbind:指令與元素解綁時呼叫,只執行一次。

注意:

1.除 update 與 componentUpdated 鉤子函式之外,每個鉤子函式都含有 el、binding、vnode 這三個引數

2.在每個函式中,第一個引數永遠是 el, 表示被綁定了指令的那個 dom 元素,這個el 引數,是一個原生的 js 物件,所以 Vue 自定義指令可以用來直接和 DOM 打交道

3.binding 是一個物件,它包含以下屬性:name、value、oldValue、expression、arg、modifiers

4.oldVnode 只有在 update 與 componentUpdated 鉤子中生效

5.除了 el 之外,binding、vnode 屬性都是隻讀的

鉤子函式說白了也就是生命週期,即當一個指令繫結到一個元素上時,這個指令內部有5個生命週期事件函式。接下來建立一個案例來看看這幾個鉤子函式的觸發情況:

<p v-test>這是一段文字</p>
​
export default {
    ... ...
    directives: {
        test: {
              bind () {
                console.log('bind')
      程式設計客棧        },inserted () {
                console.log('inserted')
              },update () {
                console.log('update')
              },componentUpdated () {
                console.log('componentUpdated')
              },unbind () {
                console.log('unbind')
              }
        }
    }
}

結果:

詳解Vue自定義指令及使用

頁面渲染時,觸發了 bind 和inserted 函式。那麼另外三個鉤子函式什麼時候會觸發呢?

關於 update 的官方解釋:

update:所在元件的 VNode 更新時呼叫,但是可能發生在其子 VNode 更新之前。指令的值可能發生了改變,也可能沒有。但是你可以通過比較更新前後的值來忽略不必要的模板更新 (詳細的鉤子函式引數見下)。

有點疑惑,‘ 所在元件的 VNode ' 是指當前綁定了該指令的 dom 元素嗎?如果是的話,是不是隻要當前元素的狀態發生了變化就會觸發 update 呢?如下通過 v-show 來切換元素顯示隱藏:

<p v-test v-show="show">這是另外一段文字</p>
<button @click="show = !show">toggle</button>

export default {
  data () {
    return {
      show: true
    }
  }
}

預設還是觸發 bind 和inserted ,當點選按鈕切換元素的 display 時,結果如下:

詳解Vue自定義指令及使用

即:改變元素的樣式的時候觸發了 update 和componentUpdated 函式。如果使用 v-if 會觸發哪個事件呢?

<p v-test v-if="show">這是另外一段文字</p>
<button @click="show = !show">toggle</button>

結果:

詳解Vue自定義指令及使用

發現 unbind 被觸發,因為 v-if 是刪除或者重建 dom 元素,當指令繫結的元素被銷燬時,會觸發指令的 unbind 事件,新建顯示仍是觸發 bind 和inserted 。

總結:

  • bind():當指令繫結在 HTML 元素上時觸發
  • inserted():當指令繫結的元素插入到父節點中的時候觸發
  • update():當指令繫結的元素狀態/樣式、內容(這裡指元素繫結的 vue 資料) 發生改變時觸發
  • componentUpdated():當 update() 執行完畢之後觸發
  • unbind():當指令繫結的元素從 dom 中刪除時觸發

舉幾個應用場景的栗子

1、輸入框自動獲取焦點(官方示例)。

2、點選下拉選單以外的區域隱藏選單。

3、輸入的郵箱、電話的校驗。

上面這幾個場合,在做專案的時候可以用其他方法代替,但是 vue 自定義指令能做到一處定義,全域性呼叫,使得程式碼簡潔高效,更便於維護。

指令的註冊方式和「過濾器」「混入」「元件」註冊的方式一樣都分為兩種:一是全域性註冊,二是區域性註冊。

三、全域性指令

// 給元素新增隨機背景
<div v-bgcolor></div>
   
Vue.directive('bgcolor',{
    bind: function(el,binding,vnode) {
        el.style.backgroundColor = "#" + Math.random().toString(16).slice(2,8);
    }
})

注意:在定義的時候,指令的名稱前面不需要加 v- 字首,在呼叫的時候,必須在指定的名稱前加上 v-字首來進行呼叫

四、區域性指令

// 和data,methods同級
methods: {},directives: {
    bgcolor: {
         bind: function(el,bindi程式設計客棧ng) {
            el.style.backgroundColor = "#" + Math.random().toString(16).slice(2,8);
        }
    }
}

我個人更傾向於使用全域性註冊的方式,因為既然已經使用了自定義指令,應該是通用的可複用的。所以提供整個專案使用的指令才更有價值,而不僅僅只限於某個元件內部。如果單一地方使用直接把功能摟出來就行了,何必費這力氣。

五、帶引數的自定義指令

<div v-bgcolor='{color: 'orange'}'></div>

Vue.directive('bgcolor',binding) {
        el.style.backgroundColor = binding.value.color;
    }
})

六、函式簡寫

如果想在 bind 和 update 時觸發相同行為,而不關心其它的鉤子,可以這樣寫:

// 全域性
Vue.directive('bgcolor',function (el,binding) {
      el.style.backgroundColor = binding.value
})

// 區域性
directives: {
    bgcolor: (el,binding) => {
        el.style.backgroundColor = binding.value  
    }
}

七、應用例項

熟悉指令的建立方式與引數之後,我們就用它來建立兩個實際案例。

通過指令來實現點選空白處子選單隱藏的功能,具體程式碼如下:

// clickOutside.js
export default {
    bind (el,binding) {
        const self = {} // 定義一個私有變數,方便在unbind中可以解除事件監聽
        self.documentHandler = (e) => {
            if (el.contains(e.target)) { // 這裡判斷繫結的元素是否包含點選元素,如果包含則返回
                return false
            }
            if (binding.value)程式設計客棧 { // 判斷指令中是否綁定了值
                binding.value(e) // 如果綁定了函式則呼叫那個函式,此處 binding.value就是handleClose方法
            }
            return true
        }
        document.addEventListener('click',self.documentHandler)
    },unbind (el) {
        // 解除事件監聽
        document.removeEventListener('click',self.documentHandler)
        delete self.documentHandler
    }
}

在元件中使用:

<template>
    <div>
        <div v-show="isShow" v-clickoutside="handleClickOutside" @click="showOrHide">
            子選單 ... 
        </div>
    </div>
</template>
​
<script>
    import clickoutside from './js/clickOutside'
    
    export default {
        ... ...
        directives程式設計客棧: {
            clickoutside
        },data() {
            return {
                isShow: true,};
        },methods: {
            handleOutsideClick () {
                this.isShow = false
            }
        }
    }
</script>

自定義指令來優化圖片的載入:圖片在載入過程中,未載入完成時使用灰色背景佔位,圖片載入完後直接顯示

<template>
    <div>
        <!-- 引數不可以直接填寫url地址-->
        <img v-imgUrl='url' /> 
    </div>
</template>
​
<script>
    export default {
        data () {
            return {
                url: '../src/assets/logo.png'
            }
        }
    }
</script>

// 全域性註冊
Vue.directive('imgUrl',binding) {
    el.style.backgroundColor = '#FEFEFE' //設定背景顏色
    var img = new Image()
    img.src = binding.value // binding.value 指令後的引數
    img.onload = function () {
        el.style.backgroundColor = ''
        el.src = binding.value
    }
})

以上就是詳解Vue自定義指令及使用的詳細內容,更多關於Vue的資料請關注我們其它相關文章!