Vue不僅內置了各類指令,包括條件渲染、事件處理等,還能注冊自定義指令。
## 一、條件渲染
  條件渲染的指令包括v-if、v-else、v-else-if和v-show。
**1)v-if**
  該指令的功能和條件語句中的if類似,可根據表達式的計算結果,判斷是否渲染分支中的元素和它所包含的子元素。在下面的示例中,當把數據對象的exist屬性設為true時,\<div>和\<p>兩個元素就會被添加到頁面的DOM中。
~~~html
<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>元素。
~~~html
<template v-if="exist">
<p>strick</p>
<p>freedom</p>
</template>
~~~
**2)v-else**
  該指令的功能和條件語句中的else類似,需要與v-if配合使用,并且要緊跟在v-if或v-else-if之后(如下所示),否則該指令將失效。
~~~html
<p v-if="exist">strick</p>
<p v-else>freedom</p>
~~~
**3)v-else-if**
  該指令與v-else類似,也需要緊跟在v-if或v-else-if之后,但它能自定義條件表達式,如下所示。
~~~html
<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;”,如下所示。
~~~html
<!-- <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有更高的初始渲染開銷。
## 二、列表渲染
  v-for是一個用于列表渲染的指令,它能接收數組、對象和整數,并且支持\<template>元素。
**1)數組**
  當基于數組來渲染一個列表時,v-for指令可迭代數組的元素和其索引。下面是一個示例,在第一組\<li>元素中,item是元素的別名,迭代語法和for-in循環語句類似;在第二組\<li>元素中,向v-for指令傳遞了兩個參數,其中第二個參數表示元素的索引。
~~~html
<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迭代器的語法,如下所示。
~~~html
<li v-for="item of array">
{{item}}
</li>
~~~
**2)對象**
  當基于對象來渲染一個列表時,v-for指令可迭代對象的屬性(即鍵值對)和其索引。在下面的示例中,為v-for指令傳遞了三個參數,其中屬性值在前,屬性名在后。
~~~html
<li v-for="(value, key, index) in obj">
{{index}} - {{key}} - {{value}}
</li>
~~~
**3)整數**
  v-for指令接收的整數,可用于循環次數,如下所示,其中digit從1開始計數。
~~~html
<li v-for="digit in 3">
{{digit}}
</li>
~~~
**4)key特性**
  Vue為元素提供了一個能標識其身份的[key特性](https://cn.vuejs.org/v2/api/#key),利用該特性可讓diff算法快速找到變化的節點,并且高效的將其插入到新位置,而不用渲染無變化的元素。可以像下面這樣為每個\<li>元素設置一個key特性,其值為peoples數組中的元素對象的id屬性。
~~~html
<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重復,從而造成渲染錯誤。
~~~html
<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指令進行切換時,其內容都會被清空。
~~~html
<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()方法,就會將新增的對象渲染到頁面中。
~~~js
vm.peoples.push({id:3, name:"justify"});
~~~
  對于那些filter()、concat()、slice()等非變異方法,可通過替換數組來觸發視圖的更新,如下所示。
~~~js
vm.peoples = vm.peoples.slice(0, 1);
~~~
## 三、事件處理
Vue提供了用于注冊事件的[v-on](https://cn.vuejs.org/v2/api/#v-on)指令,它的參數是事件類型,接收的表達式既可以是方法名,也可以是內聯語句,并且能與多個修飾符配合使用。
**1)v-on**
  當接收一個方法名時,默認會將事件對象作為第一個參數傳遞給事件處理程序,例如下面dot()方法中的e參數。
~~~html
<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變量傳遞事件對象,并且可聲明在形參列表的任意位置,如下所示。
~~~html
<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:”,如下代碼所示,后文的示例都會使用該形式。
~~~html
<button @click="dot">點擊</button>
~~~
  如果要在同一個事件類型中執行多個事件處理程序,那么可以像下面這樣用逗號把兩條內聯語句隔開。
~~~html
<button @click="dot1(),dot2()">點擊</button>
~~~
**2)修飾符**
  v-on支持多種類型的修飾符(包括事件、按鍵、鼠標等),在事件細節的處理方面,提供了更多的選擇,例如可用的事件修飾符如表1所列。
:-: 
表1 事件修飾符
  當串聯多個修飾符時,會按順序影響事件的處理,即不同的順序會產生不同的效果。下面用一個示例來演示不同排列所造成的差異,其中print()方法用于打印接收的參數,\<section>元素添加了.stop和.self兩個修飾符。
~~~html
<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)的修飾符形式。
~~~html
<input @keyup.arrow-down="enter" />
~~~
  不僅如此,通過事件對象的keyCode屬性得到的建碼也能作為修飾符使用,例如ArrowDown的建碼是40,可以像下面這樣聲明。
~~~html
<input @keyup.40="enter" />
~~~
  由于鍵碼不便記憶,因此Vue提供了常用鍵碼的別名,例如.enter、.tab、.space、.down等。對于那些不常用的建碼,可通過Vue.config.keyCodes自定義,例如為F12鍵創建別名,如下所示。
~~~js
Vue.config.keyCodes.f12 = 123;
~~~
  有一點要注意,keyCode已從Web標準中移除,在未來可能也會被最新的瀏覽器廢棄,因此要慎用該類修飾符。
  Vue 2.1.0新增了四個特殊的系統修飾鍵:.ctrl、.alt、.shift和.meta,其中.meta會隨著操作系統的不同而對應不同的鍵,例如在Mac系統中對應command鍵,而在Windows系統中對應Windows圖標鍵。與按鍵修飾符不同,在與keyup事件配合時,系統修飾鍵必須處于按下狀態,否則無法觸發事件。以下面的文本框為例,如果要執行回調函數enter(),不僅要按一下ArrowDown鍵,還得同時按住Ctrl鍵。
~~~html
<input @keyup.arrow-down.ctrl="enter" />
~~~
  Vue 2.5.0新增了一個特殊的.exact修飾符,用于控制系統修飾鍵的組合。在下面的示例中,第一個文本框中的事件,可通過單獨按下Ctrl鍵或與其它修飾鍵組合觸發;第二個文本框添加了.exact修飾符,只有按下Ctrl鍵才會觸發事件,無法與其它修飾鍵組合。
~~~html
<input @keyup.ctrl="enter" />
<input @keyup.ctrl.exact="enter" />
~~~
  Vue 2.2.0新增了三個用于鼠標事件的修飾符:.left、.right和.middle,分別對應三種鼠標按鍵狀態:左鍵,右鍵和中鍵。
## 四、自定義指令
  由于內置指令無法滿足所有場景,因此Vue允許用戶注冊自定義的指令,以便封裝特殊的DOM行為,提升代碼復用率。
**1)注冊**
  可在實例的directives選項中注冊局部指令,例如為文本框自動輸入一段字符,如下代碼所示。其中autoEnter是指令名稱,它的值是一個指令的定義對象,注意,在元素中使用該自定義指令時,不能采用駝峰的方式。
~~~html
<input v-auto-enter/>
<script>
var vm = new Vue({
directives: {
autoEnter: {
inserted: function(el) {
el.value = "strick";
}
}
}
});
</script>
~~~
  也可以在創建Vue實例之前,通過Vue.directive()方法注冊全局指令,如下所示。
~~~js
Vue.directive("autoEnter", {
inserted: function(el) {
el.value = "strick";
}
});
var vm = new Vue({...});
~~~
**2)定義對象**
  在指令的定義對象中,有5個鉤子函數,其使用方法如表2所述。
:-: 
表2 鉤子函數
  接下來自定義一個v-hooks指令,并為其添加4個鉤子函數,作用于\<div>元素,如下所示,其中\<p>是\<div>的子元素。
~~~html
<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"。
~~~js
vm.name = "freedom";
~~~
  此時再修改子元素\<p>中的插值(如下代碼所示),那么仍然會調用update()函數,只是指令的新值和舊值都是“freedom”。
~~~js
vm.age = 30;
~~~
  當通過v-if指令銷毀元素時(如下代碼所示),就會調用unbind()函數,并輸出“unbind”。
~~~js
vm.display = false;
~~~
  當bind()和update()觸發的行為相同時,Vue允許將指令的定義對象聲明成一個函數,如下所示。
~~~js
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對象
  自定義指令可接收任意合法的JavaScript表達式,包括對象,并且還支持動態參數,如下所示。
~~~html
<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>
~~~
*****
> 原文出處:
[博客園-Vue躬行記](https://www.cnblogs.com/strick/category/1512864.html)
[知乎專欄-Vue躬行記](https://zhuanlan.zhihu.com/pwvue)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020