1. 程式人生 > >【Vue】通過自定義指令回顧 v-內建指令

【Vue】通過自定義指令回顧 v-內建指令

Vue.js 的各種指令(Directives)更加方便我們去資料驅動 DOM,例如 v-bind、v-on、v-model、v-if、v-for、v-once 等內建指令,這些指令的職責就是當表示式改變時將某些行為應用到 DOM 上,儘量不去操作增刪改 DOM。通過了解如何去自定義指令,可以想象內建指令是如何完成的。

一、自定義指令

在需要特殊功能時,使用自定義指令對 DOM 進行底層操作

1.1 註冊

自定義指令的註冊分為全域性註冊區域性註冊,類似元件的註冊,只是方法名為 directive,寫法如下:


// 全域性註冊 自定義指令
Vue.directive(‘mydir’,{
    // 指令選項
});
// 全域性註冊 自定義指令函式
Vue.directive('mydir', function () {
  // 這裡將會被 `bind` 和 `update` 呼叫
})
// 區域性註冊(只針對元件內元素)
export default {
    directives: {
        mydir: {
            // 指令選項
        }
    }
}
需要注意的是: Vue.directive( ) 註冊指令要在例項初始化 new Vue( ) 之前才能全域性註冊指令。定義指令時駝峰式寫法會報錯,所以一般小寫。

1.2 指令選項

自定義指令的選項是由幾個鉤子函式(可選)組成,可以根據需求選擇不同的鉤子,例如使用全域性註冊一個指令時:


Vue.directive('mydir', {
  bind: function () {
    // 只調用一次,指令第一次繫結到元素時呼叫,用於在繫結元素時執行一次的初始化動作。
    },
  update: function () {
    // 第一次是緊跟在 bind 之後呼叫,獲得的引數是繫結的初始值,
    // 之後被繫結元素所在的模板更新時呼叫,而不論繫結值是否變化,可以忽略不必要的模板更新。    
    },  
  inserted: function () {
    // 被繫結元素插入父節點時呼叫(父節點存在即可呼叫,不必存在於 document 中)。
    },
  componentUpdated: function () {
    // 被繫結元素所在模板完成一次更新週期時呼叫。
    },
  unbind: function () {
    // 只調用一次, 指令與元素解綁時呼叫。
    }
})

以上每個鉤子函式都有幾個引數可用:

  • el:指令所繫結的元素,可以用來直接操作 DOM;
  • binding:包含指令資訊的一個物件;
  • vnode:Vue 編譯的生成虛擬節點;
  • oldVnode:上一次的虛擬節點,僅在update和componentUpdated鉤子函式中可用。

1.3 示例


// 一個帶自定義指令的元素
<div v-mytest:foo.m1.m2="1+1">MyDirective</div>

// 部分 JS 程式碼
export default {
    directives:{
      mytest: {
        bind: function (el, binding, vnode) {
          console.log(el)
          console.log(binding)
          console.log(vnode)
        }
      }
    }
}

控制檯輸出截圖:

在這裡插入圖片描述

其中對於 binding 物件輸出的屬性有:

  • rawName: "v-mytest:foo.m1.m2" // 自定義指令
  • name: "mytest" // 指令名稱
  • arg: "foo" // 指令的引數
  • modifiers: {m1: true, m2: true} // 指令的修飾符
  • expression: "1+1" // 指令繫結值的字串形式
  • value: 2 // 指令的繫結值

二、v-bind || : 繫結屬性

Vue 內建指令 v-bind 用於動態更新 HTML 元素屬性,使用 v-bind:someAttr = "someData"或者語法糖 :someAttr = "someData"就可以在 someData 改變時更新繫結的 someAttr 屬性。

2.1 基本用法

繫結單一的屬性值


<a :href="url" :id="linkID">連結</a>

測試 data 如下:


// js
data : {
    url: 'https://www.baidu.com/',
    linkID : 'myid'
}

元素渲染輸出:
<a href="https://www.baidu.com/" id="myid">連結</a>

2.2 物件語法

v-bind 最常用的是繫結 class 或 style 屬性來動態改變樣式。例如可以給 :class 設定一個物件來動態切換 class 的值:


&lt;!-- class 繫結 --&gt;
&lt;div :class="{colorRed: isRed}"&gt;&lt;/div&gt;

當 isRed:true 時渲染輸出:
<div class="colorRed"></div>

物件中可以傳入多個屬性值來動態切換 class:


&lt;!-- class 繫結,傳入多個屬性 --&gt;
&lt;div :class="{ classA: isA, classB: isB }"&gt;

當 isA、isB 變化時 classA、classB 會動態更新,當都為 true 時顯然渲染結果為:
<div class="classA classB"></div>

同理對於 style 可以傳入物件屬性,並且可以使用字串拼接:


&lt;!-- style 繫結 --&gt;
&lt;div :style="{ fontSize: size + 'px' }"&gt;&lt;/div&gt;

對於元素中的各個物件可以統一用 v-bind 繫結:


&lt;!-- 繫結一個有屬性的物件 --&gt;
&lt;div v-bind="{ id: someProp, 'other-attr': otherProp }"&gt;&lt;/div&gt;

2.3 陣列語法

class 可以傳入多值,給 :class 繫結一個數組就可以使用 class 列表


&lt;div :class="[activeA, activeB]"&gt;&lt;/div&gt;

例如當 {activeA: 'class1', activeB: 'class2'} 時渲染結果為:
<div class="class1 class2"></div>

還可以在陣列語法中使用三元表示式切換 class,例如:


&lt;div :class="[isA ? activeA : '', activeB]"&gt;

在 class 有多個條件時使用三元表示式比較繁瑣,可以在陣列語法中使用物件語法


&lt;div :class="[{activeA: isA}, activeB]"&gt;

2.4 修飾符

v-bind 的修飾符很少,API 中只提供.prop.camel.sync,並且多用於元件,使用方式示例:


&lt;!-- 通過 prop 修飾符繫結 DOM 屬性 (property) --&gt;
&lt;div v-bind:text-content.prop="text"&gt;&lt;/div&gt;

&lt;!-- .camel 修飾符(2.1.0+)將 v-bind 屬性名稱 kebab-case 駝峰化為 camelCase --&gt;
&lt;svg :view-box.camel="viewBox"&gt;&lt;/svg&gt;

&lt;!-- .sync 修飾符(2.3.0+) 語法糖,會擴充套件成一個更新父元件繫結值的 v-on 偵聽器--&gt;
&lt;text-document v-bind:title.sync="doc.title"&gt;&lt;/text-document&gt;
&lt;!-- 批量繫結,將 doc 物件中的每一個屬性 (如 title) 都作獨立的 prop ,各自新增 v-on 監聽器--&gt;
&lt;text-document v-bind.sync="doc"&gt;&lt;/text-document&gt;

三、v-on || @ 監聽事件

v-on 用於動態繫結事件監聽器,使用 v-on:someEvent = "someFunction"或者語法糖 @someEvent = "someFunction"就可以監聽 someEvent 進行互動。

3.1 基本用法

@someEvent 呼叫的方法名後面可以不跟(),例如:


&lt;a :href="url" :id="linkID"&gt;連結&lt;/a&gt;
&lt;!-- 監聽一個事件 --&gt;
&lt;button @click="changeFun"&gt;change button&lt;/button&gt;

可以在 methods 中新增函式:


// 部分 JS 程式碼
methods :{
  changeFun : function () {
    this.linkID = 'changeID' // 指向當前元件本身
  }
}

點選 button 按鈕後 a 元素的 id 改變:
<a href="https://www.baidu.com/" id="changeID">連結</a>

當然 v-on 還可以使用物件語法監聽多個事件:


&lt;!-- v2.4.0+ --&gt;
&lt;button v-on="{ mousedown: doThis, mouseup: doThat }"&gt;&lt;/button&gt;
對於在 HTML 元素上監聽的事件,當 ViewModel 銷燬時,所有的事件處理器會自動刪除,無需自己清理。

3.2 修飾符

Vue 可以將原生事件物件引數 event 傳入事件方法中,並提供了特殊變數$event用來訪問元素 DOM 事件。此外可以通過一些事件修飾符來實現特定的事件,如 .stop.prevent.capture.once 等,常用的使用示例:


&lt;!-- 停止單擊事件冒泡,呼叫 event.stopPropagation()--&gt;
&lt;button @click.stop="doThis"&gt;&lt;/button&gt;

&lt;!-- 阻止預設行為,呼叫 event.preventDefault() --&gt;
&lt;button @submit.prevent="doThis"&gt;&lt;/button&gt;

&lt;!-- 新增事件偵聽器時使用 capture 事件捕獲模式 --&gt;
&lt;button @click.capture="doThis"&gt;&lt;/button&gt;

&lt;!-- 點選回撥只會觸發一次 --&gt;
&lt;button @click.once="doThis"&gt;&lt;/button&gt;

&lt;!-- 只當點選滑鼠左鍵時觸發(2.2.0)  --&gt;
&lt;button @click.left="doThis"&gt;&lt;/button&gt;

&lt;!-- 串聯修飾符 --&gt;
&lt;button @click.stop.prevent="doThis"&gt;&lt;/button&gt;

此外,v-on 還提供按鍵修飾符來監聽鍵盤事件,鍵值為 .keyCode,常用有.entry.delete.tab.esc.space.down等,如下:


&lt;!-- 只有在 `keyCode` 是 5 時呼叫 `vm.submit()` --&gt;
&lt;input v-on:keyup.5="submit"&gt;

&lt;!-- 為重要的 keyCode 如 enter 提供別名--&gt;
&lt;input v-on:keyup.enter="submit"&gt;

&lt;!-- 縮寫語法 --&gt;
&lt;input @keyup.enter="submit"&gt;
此外還有 系統修飾符監聽鍵盤事件,不同的系統其鍵盤/系統修飾符不一樣。這些按鍵修飾符可以任意組合使用。

四、v-if、v-show 條件渲染

條件渲染 v-if 根據表示式的值的真假條件渲染元素,在表示式為真時渲染,為假時移除。


&lt;p v-if="status === 1"&gt;當 status 為 1 時顯示此行&lt;/p&gt;

&lt;p v-else-if="status === 1"&gt;當 status 為 2 時顯示此行&lt;/p&gt;

&lt;p v-else&gt;其它情況預設顯示此行&lt;/p&gt;

v-show 也是條件渲染,但只切換元素的 CSS 屬性 display,無論條件真假都會被編譯,相比於 v-if 更適用於頻繁切換場景。


&lt;p v-show="status === 1"&gt;當 status 為 1 時顯示此行&lt;/p&gt;

當 data: {status: 2} 時隱藏,但依舊會被編譯,渲染結果為:
<p style="display: none;">當 status 為 1 時顯示此行</p>

顯然在 Vue.js 內建的 <template> 元素上可以使用 v-if,但不能使用 v-show,可以思考下為什麼。

五、v-for 列表渲染

列表渲染指令 v-for 常用於陣列遍歷或列舉一個物件的迴圈顯示,必須結合 in 使用特定語法 alias in expression 為當前遍歷的元素提供別名:


&lt;!-- 遍歷一個數組 --&gt;
&lt;div v-for="item in items"&gt;{{ item.text }}&lt;/div&gt;

&lt;!-- 提供第二個的引數為陣列的索引 --&gt;
&lt;div v-for="(item, index) in items"&gt;{{ index }} - {{ item.text }}&lt;/div&gt;

&lt;!-- 遍歷物件屬性 --&gt;
&lt;div v-for="value in object"&gt;{{ value }}&lt;/div&gt;

&lt;!-- 提供第二個可選的引數:物件的鍵名 --&gt;
&lt;div v-for="(value, key) in object"&gt;{{ key }}: {{ value }}&lt;/div&gt;

&lt;!-- 提供第三個的可選引數:物件的索引 --&gt;
&lt;div v-for="(value, key, index) in object"&gt;{{ index }}. {{ key }}: {{ value }}&lt;/div&gt;
可以用 of 替代 in 作為分隔符

v-forv-if 在同一節點一起使用時,v-for 的優先順序比 v-if 更高。

六、v-model 表單控制元件雙向繫結

v-model 其實也是一個特殊的語法糖,其實實現的資料雙向繫結也可用v-bindv-on實現,但v-model在不同表單上會有更加智慧的處理。

6.1 文字框

經典的使用案例是對<input><textarea>文字框的雙向資料繫結:


&lt;!-- 輸入框 --&gt;
&lt;input type="text" v-model="message" placeholder="edit me"&gt;
&lt;!-- 文字域 --&gt;
&lt;textarea v-model="message" placeholder="edit me"&gt;&lt;/textarea&gt;
&lt;!-- 實時更新 --&gt;
&lt;p&gt;Message is: {{ message }}&lt;/p&gt;

6.2 動態選擇

對於單選按鈕,複選框及選擇框的選項,v-model配合 Vue 例項的資料作為value屬性值實現不同效果,即會忽略所有表單元素的 value、checked、selected 特性的值。


&lt;!--單選按鈕的互斥效果--&gt;
&lt;div id="example-radio"&gt;
  &lt;input type="radio" id="one" value="One" v-model="picked"&gt;
  &lt;label for="one"&gt;One&lt;/label&gt;
  
  &lt;input type="radio" id="two" value="Two" v-model="picked"&gt;
  &lt;label for="two"&gt;Two&lt;/label&gt;
  
  &lt;!-- picked 顯示的是 value 的值 --&gt;
  &lt;p&gt;Picked: {{ picked }}&lt;/p&gt;
&lt;/div&gt;

&lt;!--多選按鈕--&gt;
&lt;div id='example-checkbox'&gt;
  &lt;input type="checkbox" id="one" value="One" v-model="checkedNames"&gt;
  &lt;label for="jack"&gt;Jack&lt;/label&gt;
  &lt;input type="checkbox" id="two" value="Two" v-model="checkedNames"&gt;
  &lt;label for="john"&gt;John&lt;/label&gt;

    &lt;!-- Checked 顯示的是 value 組成的陣列 --&gt;
  &lt;p&gt;Checked: {{ checkedNames }}&lt;/p&gt;
&lt;/div&gt;

6.3修飾符

v-model的修飾符的使用限制在<input>、<select>、<textarea> 和元件。

  • .lazy - 取代 input 監聽 change 事件
  • .number - 輸入字串轉為數字
  • .trim - 輸入首尾空格過濾

七、v-pre、v-cloak、v-once

這三個指令的共同點是無需表示式,用法如下:


&lt;!-- 不顯示未編譯的標籤直到例項初始化完 --&gt;
&lt;div v-cloak&gt;{{ message }}&lt;/div&gt;
&lt;!-- 需要配合 CSS 隱藏樣式 [v-cloak]{ display: none;}--&gt;

&lt;!-- 只渲染一次,隨後的渲染將被視為靜態內容並跳過 --&gt;
&lt;div v-once&gt;{{ message }}&lt;/div&gt;

&lt;!-- 不會被編譯,直接顯示顯示原始{{ }}標籤 --&gt;
&lt;div v-pre&gt;{{ message }}&lt;/div&gt;

繼續加油鴨~ 少年!

來源: