Vue躬行記(2)——指令

  Vue不只內置了各種指令,包括條件渲染、事件處理等,還能註冊自定義指令。vue

1、條件渲染

  條件渲染的指令包括v-if、v-else、v-else-if和v-show。node

1)v-ifgit

  該指令的功能和條件語句中的if相似,可根據表達式的計算結果,判斷是否渲染分支中的元素和它所包含的子元素。在下面的示例中,當把數據對象的exist屬性設爲true時,<div>和<p>兩個元素就會被添加到頁面的DOM中。算法

<div v-if="exist">
  <p>strick</p>
</div>
<script>
  var vm = new Vue({ 
    data: {  
      exist: true 
    }
  });
</script>

  當須要經過v-if指令渲染多個元素,而且沒必要指定包裹元素時,就得使用Vue提供的<template>元素了,以下代碼所示。其功能相似於React的Fragments,也可生成一個不可見的包裹元素,而且在最終的DOM中,不會包含<template>元素。express

<template v-if="exist">
  <p>strick</p>
  <p>freedom</p>
</template>

2)v-elseapi

  該指令的功能和條件語句中的else相似,須要與v-if配合使用,而且要緊跟在v-if或v-else-if以後(以下所示),不然該指令將失效。數組

<p v-if="exist">strick</p>
<p v-else>freedom</p>

3)v-else-if瀏覽器

  該指令與v-else相似,也須要緊跟在v-if或v-else-if以後,但它能自定義條件表達式,以下所示。dom

<p v-if="digit==1">strick</p>
<p v-else-if="digit==2">justify</p>
<p v-else>freedom</p>

4)v-show函數

  該指令能根據表達式的計算結果顯示或隱藏當前元素,若是獲得的結果是一個假值,那麼會爲元素添加內聯樣式「display: none;」,以下所示。

<!-- <p style="display: none;">strick</p> -->
<p v-show="false">strick</p>

  v-if會在切換過程當中建立或銷燬可能包含的數據綁定和子元素,而且只有在條件爲真時,纔會渲染該分支中的元素。而v-show不管條件的真假都會渲染元素,很適合頻繁切換,而且要注意,v-show既不能做用於<template>元素,也沒法與v-else配合。由前面的分析可知,v-if有更高的切換開銷而v-show有更高的初始渲染開銷。

2、列表渲染

  v-for是一個用於列表渲染的指令,它能接收數組、對象和整數,而且支持<template>元素。

1)數組

  當基於數組來渲染一個列表時,v-for指令可迭代數組的元素和其索引。下面是一個示例,在第一組<li>元素中,item是元素的別名,迭代語法和for-in循環語句相似;在第二組<li>元素中,向v-for指令傳遞了兩個參數,其中第二個參數表示元素的索引。

<ul id="container">
  <li v-for="item in array">
    {{item}}
  </li>
  <li v-for="(item, index) in array">
    {{index}} - {{item}}
  </li>
</ul>
<script>
  var vm = new Vue({ 
    el: "#container",
    data: {  
      array: ["strick", "freedom"]
    }
  });
</script>

  v-for指令中的in還能夠替換成of,從而更接近ES6迭代器的語法,以下所示。

<li v-for="item of array">
  {{item}}
</li>

2)對象

  當基於對象來渲染一個列表時,v-for指令可迭代對象的屬性(即鍵值對)和其索引。在下面的示例中,爲v-for指令傳遞了三個參數,其中屬性值在前,屬性名在後。

<li v-for="(value, key, index) in obj">
  {{index}} - {{key}} - {{value}}
</li>

3)整數

  v-for指令接收的整數,可用於循環次數,以下所示,其中digit從1開始計數。

<li v-for="digit in 3">
  {{digit}}
</li>

4)key特性

  Vue爲元素提供了一個能標識其身份的key特性,利用該特性可以讓diff算法快速找到變化的節點,而且高效的將其插入到新位置,而不用渲染無變化的元素。能夠像下面這樣爲每一個<li>元素設置一個key特性,其值爲peoples數組中的元素對象的id屬性。

<ul id="container">
  <li v-for="item in peoples" v-bind:key="item.id">
    {{item}}
  </li>
</ul>
<script>
  var vm = new Vue({
    el: "#container",
    data: {
      peoples: [
        { id: 1, name: "strick" },
        { id: 2, name: "freedom" }
      ]
    }
  });
</script>

  注意,兄弟元素之間的key特性要保持惟一性,即相同父元素的子元素,其key特性要獨一無二。下面這樣會讓key重複,從而形成渲染錯誤。

<ul id="container">
  <li v-for="item in peoples" v-bind:key="item.id">
    {{item}}
  </li>
  <li v-for="item in peoples" v-bind:key="item.id">
    {{item}}
  </li>
</ul>

  爲了渲染高效,Vue一般會複用已有元素而不是從新渲染,但若是要強制替換元素,那麼可使用key特性。例若有兩個文本框,以下代碼所示,因爲兩個文本框都聲明瞭key特性,所以每次經過v-if指令進行切換時,其內容都會被清空。

<input placeholder="enter name" key="name" v-if="display"/>
<input placeholder="enter age" key="age" v-else/>

5)數組更新檢測

  Vue包裝了多個會修改原始數組的變異方法(Mutation Method),例如push()、shift()、splice()等,每當調用這些方法時,都會觸發視圖更新。在下面的示例中,vm是一個Vue實例,peoples是一個數組,一旦調用push()方法,就會將新增的對象渲染到頁面中。

vm.peoples.push({id:3, name:"justify"});

  對於那些filter()、concat()、slice()等非變異方法,可經過替換數組來觸發視圖的更新,以下所示。

vm.peoples = vm.peoples.slice(0, 1);

3、事件處理

  Vue提供了用於註冊事件的v-on指令,它的參數是事件類型,接收的表達式既能夠是方法名,也能夠是內聯語句,而且能與多個修飾符配合使用。

1)v-on

  當接收一個方法名時,默認會將事件對象做爲第一個參數傳遞給事件處理程序,例以下面dot()方法中的e參數。

<button v-on:click="dot">點擊</button>
<script>
  var vm = new Vue({
    data: {
      name: "strick"
    },
    methods: {
      dot: function(e) {
        console.log(e, this.name);
      }
    }
  });
</script>

  當接收一條內聯語句時,可經過一個特殊的$event變量傳遞事件對象,而且可聲明在形參列表的任意位置,以下所示。

<button v-on:click="dot(28, $event, 'university')">點擊</button>
<script>
  var vm = new Vue({
    data: {
      name: "strick"
    },
    methods: {
      dot: function(age, e, school) {
        console.log(e, this.name, age, school);
      }
    }
  });
</script>

  v-on還有一種簡寫形式,用「@」符號替代「v-on:」,以下代碼所示,後文的示例都會使用該形式。

<button @click="dot">點擊</button>

  若是要在同一個事件類型中執行多個事件處理程序,那麼能夠像下面這樣用逗號把兩條內聯語句隔開。

<button @click="dot1(),dot2()">點擊</button>

2)修飾符

  v-on支持多種類型的修飾符(包括事件、按鍵、鼠標等),在事件細節的處理方面,提供了更多的選擇,例如可用的事件修飾符如表1所列。

表1  事件修飾符

修飾符 做用
.stop 調用事件對象的stopPropagation()方法
.prevent 調用事件對象的preventDefault()方法
.capture 採用捕獲的事件傳播形式
.self 只有當事件是由註冊的元素觸發的,即event.target等於綁定該事件的元素,才執行回調
.once 事件只會觸發一次
.passive 永不調用preventDefault()方法,注意,不能與.prevent一塊兒使用

  當串聯多個修飾符時,會按順序影響事件的處理,即不一樣的順序會產生不一樣的效果。下面用一個示例來演示不一樣排列所形成的差別,其中print()方法用於打印接收的參數,<section>元素添加了.stop和.self兩個修飾符。

<div @click="print('div')">
  <section @click.stop.self="print('section')">
    <button @click="print('button')">點擊</button>
  </section>
</div>

  當將@click.stop.self做用於<section>元素時,點擊按鈕只會輸出「button」,由於先執行.stop就會阻止全部元素髮起的事件傳播;當將@click.self.stop做用於<section>元素時,點擊按鈕會依次輸出「button」和「div」,由於先執行.self就只會阻止<section>元素髮起的事件傳播。

  除了事件修飾符以外,Vue還提供了按鍵修飾符,只有當按下特定的鍵時,纔會觸發監聽的鍵盤事件。在下面的示例中,.arrow-down修飾符表示導航鍵中的向下鍵(ArrowDown)。注意,只要是KeyboardEvent.key提供的有效鍵名,都能轉換成連字符分隔(kebab-case)的修飾符形式。

<input @keyup.arrow-down="enter" />

  不只如此,經過事件對象的keyCode屬性獲得的建碼也能做爲修飾符使用,例如ArrowDown的建碼是40,能夠像下面這樣聲明。

<input @keyup.40="enter" />

  因爲鍵碼不便記憶,所以Vue提供了經常使用鍵碼的別名,例如.enter、.tab、.space、.down等。對於那些不經常使用的建碼,可經過Vue.config.keyCodes自定義,例如爲F12鍵建立別名,以下所示。

Vue.config.keyCodes.f12 = 123;

  有一點要注意,keyCode已從Web標準中移除,在將來可能也會被最新的瀏覽器廢棄,所以要慎用該類修飾符。

  Vue 2.1.0新增了四個特殊的系統修飾鍵:.ctrl、.alt、.shift和.meta,其中.meta會隨着操做系統的不一樣而對應不一樣的鍵,例如在Mac系統中對應command鍵,而在Windows系統中對應Windows圖標鍵。與按鍵修飾符不一樣,在與keyup事件配合時,系統修飾鍵必須處於按下狀態,不然沒法觸發事件。如下面的文本框爲例,若是要執行回調函數enter(),不只要按一下ArrowDown鍵,還得同時按住Ctrl鍵。

<input @keyup.arrow-down.ctrl="enter" />

  Vue 2.5.0新增了一個特殊的.exact修飾符,用於控制系統修飾鍵的組合。在下面的示例中,第一個文本框中的事件,可經過單獨按下Ctrl鍵或與其它修飾鍵組合觸發;第二個文本框添加了.exact修飾符,只有按下Ctrl鍵纔會觸發事件,沒法與其它修飾鍵組合。

<input @keyup.ctrl="enter" />
<input @keyup.ctrl.exact="enter" />

  Vue 2.2.0新增了三個用於鼠標事件的修飾符:.left、.right和.middle,分別對應三種鼠標按鍵狀態:左鍵,右鍵和中鍵。

4、自定義指令

  因爲內置指令沒法知足全部場景,所以Vue容許用戶註冊自定義的指令,以便封裝特殊的DOM行爲,提高代碼複用率。

1)註冊

  可在實例的directives選項中註冊局部指令,例如爲文本框自動輸入一段字符,以下代碼所示。其中autoEnter是指令名稱,它的值是一個指令的定義對象,注意,在元素中使用該自定義指令時,不能採用駝峯的方式。

<input v-auto-enter/>
<script>
  var vm = new Vue({
    directives: {
      autoEnter: {
        inserted: function(el) {
          el.value = "strick";
        }
      }
    }
  });
</script>

  也能夠在建立Vue實例以前,經過Vue.directive()方法註冊全局指令,以下所示。

Vue.directive("autoEnter", {
  inserted: function(el) {
    el.value = "strick";
  }
});
var vm = new Vue({...});

2)定義對象

  在指令的定義對象中,有5個鉤子函數,其使用方法如表2所述。

表2  鉤子函數

鉤子函數 調用時機 調用次數
bind() 指令首次綁定到元素時調用 一次
inserted() 綁定元素插入到父節點時調用,此時不能保證元素已在DOM中 一次
update() 綁定該指令的組件更新時調用,其子組件此時可能還未更新,而且綁定值可能有變化,也可能保持不變 屢次
componentUpdated() 綁定該指令的組件及其子組件都完成更新後調用 屢次
unbind() 指令與元素解綁時調用 一次

  接下來自定義一個v-hooks指令,併爲其添加4個鉤子函數,做用於<div>元素,以下所示,其中<p>是<div>的子元素。

<div v-if="display" v-hooks="name">
  <p>{{age}}</p>
</div>
<script>
  var vm = new Vue({
    data: {
      name: "strick",
      display: true,
      age: 28
    },
    directives: {
      hooks: {
        bind: function() {
          console.log("bind");
        },
        inserted: function() {
          console.log("inserted");
        },
        update: function(el, binding) {
          console.log("update", binding.value, binding.oldValue);
        },
        unbind: function() {
          console.log("unbind");
        }
      }
    }
  });
</script>

  當在瀏覽器中首次加載時,控制檯會依次輸出「bind」和「inserted」。

  若是修改v-hooks指令的值(以下代碼所示),那麼就會調用update()函數,並輸出指令的新值(value)和舊值(oldValue),分別是"freedom"和"strick"。

vm.name = "freedom";

  此時再修改子元素<p>中的插值(以下代碼所示),那麼仍然會調用update()函數,只是指令的新值和舊值都是「freedom」。

vm.age = 30;

  當經過v-if指令銷燬元素時(以下代碼所示),就會調用unbind()函數,並輸出「unbind」。

vm.display = false;

  當bind()和update()觸發的行爲相同時,Vue容許將指令的定義對象聲明成一個函數,以下所示。

Vue.directive("func", function (el, binding) {
  console.log("function");
});

3)參數

  鉤子函數可接收4個參數:el、binding、vnode和oldVnode,參數說明以下所列。注意,除了el以外,其它參數都是隻讀的。

  (1)el:指令所綁定的元素。

  (2)binding:一個對象,其屬性如表3所列,舉例參照的是自定義的v-hooks指令。

  (3)vnode:Vue編譯生成的虛擬節點。

  (4)oldVnode:上一個虛擬節點,只存在於update()和componentUpdated()函數中。

表3  binding對象

屬性 描述
name 指令名稱,不包含「v-」前綴,例如hooks
rawName 指令的原始名稱,包含「v-」前綴、參數和修飾符,例如v-hooks:click.once
value 指令接收的值,例如在v-hooks="name"中,讀取name屬性獲得的值爲「strick」
oldValue 指令接收的舊值,即上一個值,只在update()和componentUpdated()函數中可用
expression 字符串類型的指令表達式,例如v-hooks="name"中的「name」
arg 傳給指令的參數,例如v-hooks:click中的「click」
oldArg 傳給指令的上一個參數,與oldValue同樣,只在update()和componentUpdated()函數中可用
modifiers 包含修飾符的對象,例如在v-hooks.once.self中,修飾符對象爲{once: true, self: true}

  自定義指令可接收任意合法的JavaScript表達式,包括對象,而且還支持動態參數,以下所示。

<p v-calculate:[arg]="{left:10, right:20}"></p>
<script>
  var vm = new Vue({
    data: {
      arg: "click"
    },
    directives: {
      calculate: {
        bind: function(el, binding) {
          console.log(binding.arg);         //"click"
          console.log(binding.value);       //{left: 10, right: 20}
        }
      }
    }
  });
</script>
相關文章
相關標籤/搜索