Vue 中「自定義指令」的魅力

  • 2020 年 1 月 13 日
  • 筆記

Vue 中有許多的指令提供我們使用。它可以讓你進行一些模版的操作。

但是內置指令,在實際的開發過程中可能這些並不能滿足所有的需求。所以 Vue 給我們提供來一個靈活的方法「自定義指令」。

說自定義指令之前,先看看什麼叫「指令」。

1.指令的概念

指令是指可以控制操作 DOM 的一些小命令,通常以 v- 前綴出現的特殊特性。

例如我們經常使用的v-ifv-showv-bindv-onv-html等。

使用指令時,你可以傳遞值,字元串,也可以給指令添加參數,修飾符等等。比如:

1.傳遞值

<p v-if="isShow">你好,我是六哥</p>let vm = new Vue({  el: "#app",  data: {    isShow: true  }});"isShow">你好,我是六哥</p>  let vm = new Vue({    el: "#app",    data: {      isShow: true    }  });

2.字元串

<p v-text="'hello world'"></p>

3.添加參數

// class,style 就是傳給指令的參數<div v-bind:class="classObj"></div><div v-bind:style="classObj"></div>    <div v-bind:class="classObj"></div>  <div v-bind:style="classObj"></div>

4.修飾符

// prevent 指令的修飾符<button v-on:submit.prevent="onSubmit"></button>    <button v-on:submit.prevent="onSubmit"></button>

以上是你經常使用指令的方式,了解這些之後,它可以幫助我們更好的認識自定義指令的 一些參數問題。下面就來看看自定義指令。

2.自定義指令

指令的註冊方式和「過濾器」、「混入」、「組件」註冊的方式一樣都分為兩種:一是全局註冊,二是局部註冊。

1.全局註冊

Vue.directive('name', {})

2.局部註冊

directives: {    name: {}}      name: {}  }

然後在模版中直接使用即可。

<p v-name>你好,六哥在這</p></p>

我個人更傾向於使用全局註冊的方式,因為既然已經使用了自定義指令,應該是通用,可復用的。

所以提供整個項目使用的指令才更有價值,而不僅僅只限於某個組件內部。如果單一地方使用直接把功能摟出就行了,何必費這力氣。

繼續來看具體的實現方式。

Vue 提供了自定義指令的幾個鉤子函數:

除update 與 componentUpdated 鉤子函數之外,每個鉤子函數都含有 elbindingvnode 這三個參數。

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

參數el 就是指令綁定的 DOM 元素,而binding是一個對象,它包含一下屬性:namevalueoldValueexpressionargmodifiers

另外值得注意的一點是,除了 el 之外,bindingvnode 屬性都是只讀的。

熟悉指令的創建方式與參數之後,我們就用它來創建一個案例。

3.創建自定義指令

Loading 是項目中最常見的一個小功能,別看它功能小,但是起到的作用卻很大,手動創建一個 Loading 指令。

Vue.directive("loading", {  bind(el, binding) {    if (binding.value) {      let div = document.createElement("div");      div.className = "loading-parent";      div.innerHTML = `        <div class="loading-spinner"><i class='el-icon-loading'></i></div>      `;      el.appendChild(div);    }  },  update(el, binding) {    if (binding.value) {      let div = document.createElement("div");      div.className = "loading-parent";      div.innerHTML = `        <div class="loading-spinner"><i class='el-icon-loading'></i></div>      `;      el.appendChild(div);      el.load = div;    } else {      el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);    }  }});    bind(el, binding) {      if (binding.value) {        let div = document.createElement("div");        div.className = "loading-parent";        div.innerHTML = `          <div class="loading-spinner"><i class='el-icon-loading'></i></div>        `;        el.appendChild(div);      }    },    update(el, binding) {      if (binding.value) {        let div = document.createElement("div");        div.className = "loading-parent";        div.innerHTML = `          <div class="loading-spinner"><i class='el-icon-loading'></i></div>        `;        el.appendChild(div);        el.load = div;      } else {        el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);      }    }  });

然後我們直接就可以在模版中使用了。

<div v-loading="loading" class="box"></div>class="box"></div>

你也看出來了很多程式碼是重複的,怎麼辦呢?

Vue 中給我們提供了簡寫方式。當只有這兩個鉤子函數時bindupdate,我們可以簡寫如下。

Vue.directive("loading", function(el, binding) {  if (binding.value) {    let div = document.createElement("div");    div.className = "loading-parent";    div.innerHTML = `        <div class="loading-spinner"><i class='el-icon-loading'></i></div>      `;    el.appendChild(div);    el.load = div;  } else {    el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);  }});function(el, binding) {    if (binding.value) {      let div = document.createElement("div");      div.className = "loading-parent";      div.innerHTML = `          <div class="loading-spinner"><i class='el-icon-loading'></i></div>        `;      el.appendChild(div);      el.load = div;    } else {      el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);    }  });

我們實現了一個非常簡單的指令,但還不夠靈活,比如我想添加 loading 的背景色,修改圖標的顏色怎麼辦呢?

這時候就需要給指令傳入多個值,改造下實現背景與圖標顏色。

Vue.directive("loading", function(el, binding) {  if (binding.value) {    let div = document.createElement("div");    div.className = "loading-parent";    div.style.backgroundColor = binding.value.background;    div.style.color = binding.value.color;    div.innerHTML = `        <div class="loading-spinner"><i class='el-icon-loading'></i></div>      `;    el.appendChild(div);    el.load = div;  } else {    el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);  }});    if (binding.value) {      let div = document.createElement("div");      div.className = "loading-parent";      div.style.backgroundColor = binding.value.background;      div.style.color = binding.value.color;      div.innerHTML = `          <div class="loading-spinner"><i class='el-icon-loading'></i></div>        `;      el.appendChild(div);      el.load = div;    } else {      el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);    }  });

直接使用

<div v-loading="{color: 'white', background: '#000'}">我可以擁有更多屬性</div></div>

在文章開始我們介紹指令時,還說到指令的「參數」與「修飾符」這裡我就不多介紹了,大家不妨自己去嘗試一下吧。體驗下自定義指令的魅力。