如何輕松學(xué)會(huì)Vue基礎(chǔ)語(yǔ)法
主要內(nèi)容Vue組件自定義事件、插槽、動(dòng)態(tài)組件和異步組件、邊界處理情況、組件生命周期、過(guò)渡&動(dòng)畫(huà)、組件復(fù)用性、自定義指令、過(guò)濾器、渲染函數(shù)&JSX、Vue漸進(jìn)式和Element UI組件庫(kù)。
學(xué)習(xí)目標(biāo)

第一節(jié) Vue組件基礎(chǔ)
Vue組件自定義事件
不同于組件和 prop,事件名不存在任何自動(dòng)化的大小寫(xiě)轉(zhuǎn)換。而是觸發(fā)的事件名需要完全匹配監(jiān)聽(tīng)這個(gè)事件所用的名稱(chēng)。舉個(gè)例子,如果觸發(fā)一個(gè) camelCase 名字的事件
this.$emit('myEvent')
不同于組件和 prop,事件名不會(huì)被用作一個(gè) JavaScript 變量名或?qū)傩悦?,所以就沒(méi)有理由使用 camelCase 或 PascalCase 了。并且 v-on 事件監(jiān)聽(tīng)器在 DOM 模板中會(huì)被自動(dòng)轉(zhuǎn)換為全小寫(xiě) (因?yàn)?HTML 是大小寫(xiě)不敏感的),所以 v-on:myEvent 將會(huì)變成 v-on:myevent——導(dǎo)致 myEvent 不可能被監(jiān)聽(tīng)到。
因此,我們推薦你始終使用 kebab-case 的事件名
.sync 修飾符
在有些情況下,我們可能需要對(duì)一個(gè) prop 進(jìn)行“雙向綁定”。不幸的是,真正的雙向綁定會(huì)帶來(lái)維護(hù)上的問(wèn)題,因?yàn)樽咏M件可以修改父組件,且在父組件和子組件都沒(méi)有明顯的改動(dòng)來(lái)源。
這也是為什么我們推薦以 update:myPropName 的模式觸發(fā)事件取而代之。舉個(gè)例子,在一個(gè)包含 title prop 的假設(shè)的組件中,我們可以用以下方法表達(dá)對(duì)其賦新值的意圖:
this.$emit('update:title', newTitle)
然后父組件可以監(jiān)聽(tīng)那個(gè)事件并根據(jù)需要更新一個(gè)本地的數(shù)據(jù)屬性。例如:
<text-document
?v-bind:title="doc.title"
?v-on:update:title="doc.title = $event"></text-document>
為了方便起見(jiàn),我們?yōu)檫@種模式提供一個(gè)縮寫(xiě),即 .sync 修飾符:
<text-document v-bind:title.sync="doc.title"></text-document>
插槽
插槽內(nèi)容
Vue 實(shí)現(xiàn)了一套內(nèi)容分發(fā)的 API,這套 API 的設(shè)計(jì)靈感源自 Web Components 規(guī)范草案,將 <slot> 元素作為承載分發(fā)內(nèi)容的出口。
它允許你像這樣合成組件:
<navigation-link url="/profile">
?Your Profile</navigation-link>
然后你在 <navigation-link> 的模板中可能會(huì)寫(xiě)為:
<a
?v-bind:href="url"
?class="nav-link">
?<slot></slot></a>
當(dāng)組件渲染的時(shí)候,<slot></slot> 將會(huì)被替換為“Your Profile”。插槽內(nèi)可以包含任何模板代碼,包括 HTML:
<navigation-link url="/profile">
?<!-- 添加一個(gè) Font Awesome 圖標(biāo) -->
?<span class="fa fa-user"></span>
?Your Profile</navigation-link>
甚至其它的組件:
<navigation-link url="/profile">
?<!-- 添加一個(gè)圖標(biāo)的組件 -->
?<font-awesome-icon name="user"></font-awesome-icon>
?Your Profile</navigation-link>
如果 <navigation-link> 沒(méi)有包含一個(gè) <slot> 元素,則該組件起始標(biāo)簽和結(jié)束標(biāo)簽之間的任何內(nèi)容都會(huì)被拋棄。
編譯作用域
當(dāng)你想在一個(gè)插槽中使用數(shù)據(jù)時(shí),例如:
<navigation-link url="/profile">
?Logged in as {{ user.name }}</navigation-link>
該插槽跟模板的其它地方一樣可以訪(fǎng)問(wèn)相同的實(shí)例屬性 (也就是相同的“作用域”),而不能訪(fǎng)問(wèn) <navigation-link> 的作用域。例如 url 是訪(fǎng)問(wèn)不到的:

作為一條規(guī)則,請(qǐng)記住:
父級(jí)模板里的所有內(nèi)容都是在父級(jí)作用域中編譯的;子模板里的所有內(nèi)容都是在子作用域中編譯的。
后備內(nèi)容
有時(shí)為一個(gè)插槽設(shè)置具體的后備 (也就是默認(rèn)的) 內(nèi)容是很有用的,它只會(huì)在沒(méi)有提供內(nèi)容的時(shí)候被渲染。例如在一個(gè) <submit-button> 組件中:
<button type="submit">
?<slot></slot></button>
我們可能希望這個(gè) <button> 內(nèi)絕大多數(shù)情況下都渲染文本“Submit”。為了將“Submit”作為后備內(nèi)容,我們可以將它放在 <slot> 標(biāo)簽內(nèi):
<button type="submit">
?<slot>Submit</slot></button>
現(xiàn)在當(dāng)我在一個(gè)父級(jí)組件中使用 <submit-button> 并且不提供任何插槽內(nèi)容時(shí):
<submit-button></submit-button>
后備內(nèi)容“Submit”將會(huì)被渲染:
<button type="submit">
?Submit</button>
但是如果我們提供內(nèi)容:
<submit-button>
?Save</submit-button>
則這個(gè)提供的內(nèi)容將會(huì)被渲染從而取代后備內(nèi)容:
<button type="submit">
?Save</button>
具名插槽
有時(shí)我們需要多個(gè)插槽。例如對(duì)于一個(gè)帶有如下模板的 <base-layout> 組件:

對(duì)于這樣的情況,<slot> 元素有一個(gè)特殊的特性:name。這個(gè)特性可以用來(lái)定義額外的插槽:

一個(gè)不帶 name 的 <slot> 出口會(huì)帶有隱含的名字“default”。
在向具名插槽提供內(nèi)容的時(shí)候,我們可以在一個(gè) <template> 元素上使用 v-slot 指令,并以 v-slot 的參數(shù)的形式提供其名稱(chēng):

現(xiàn)在 <template> 元素中的所有內(nèi)容都將會(huì)被傳入相應(yīng)的插槽。任何沒(méi)有被包裹在帶有 v-slot 的 <template> 中的內(nèi)容都會(huì)被視為默認(rèn)插槽的內(nèi)容。
然而,如果你希望更明確一些,仍然可以在一個(gè) <template> 中包裹默認(rèn)插槽的內(nèi)容:

任何一種寫(xiě)法都會(huì)渲染出:

注意 v-slot 只能添加在一個(gè) <template> 上 (只有一種例外情況),這一點(diǎn)和已經(jīng)廢棄的 slot 特性不同。
作用域插槽
有時(shí)讓插槽內(nèi)容能夠訪(fǎng)問(wèn)子組件中才有的數(shù)據(jù)是很有用的。例如,設(shè)想一個(gè)帶有如下模板的 <current-user> 組件:
<span>
?<slot>{{ user.lastName }}</slot></span>
我們想讓它的后備內(nèi)容顯示用戶(hù)的名,以取代正常情況下用戶(hù)的姓,如下:
<current-user>
?{{ user.firstName }}</current-user>
然而上述代碼不會(huì)正常工作,因?yàn)橹挥?<current-user> 組件可以訪(fǎng)問(wèn)到 user 而我們提供的內(nèi)容是在父級(jí)渲染的。
為了讓 user 在父級(jí)的插槽內(nèi)容中可用,我們可以將 user 作為 <slot> 元素的一個(gè)特性綁定上去:
<span>
?<slot v-bind:user="user">
? ?{{ user.lastName }} ?</slot></span>
綁定在 <slot> 元素上的特性被稱(chēng)為插槽 prop?,F(xiàn)在在父級(jí)作用域中,我們可以給 v-slot 帶一個(gè)值來(lái)定義我們提供的插槽 prop 的名字:
<current-user>
?<template v-slot:default="slotProps">
? ?{{ slotProps.user.firstName }} ?</template></current-user>
在這個(gè)例子中,我們選擇將包含所有插槽 prop 的對(duì)象命名為 slotProps,但你也可以使用任意你喜歡的名字。
具名插槽的縮寫(xiě)
跟 v-on 和 v-bind 一樣,v-slot 也有縮寫(xiě),即把參數(shù)之前的所有內(nèi)容 (v-slot:) 替換為字符 #。例如 v-slot:header 可以被重寫(xiě)為 #header:

然而,和其它指令一樣,該縮寫(xiě)只在其有參數(shù)的時(shí)候才可用。這意味著以下語(yǔ)法是無(wú)效的:
<!-- 這樣會(huì)觸發(fā)一個(gè)警告 --><current-user #="{ user }">
?{{ user.firstName }}</current-user>
如果你希望使用縮寫(xiě)的話(huà),你必須始終以明確插槽名取而代之:
<current-user #default="{ user }">
?{{ user.firstName }}</current-user>
動(dòng)態(tài)組件 & 異步組件
在動(dòng)態(tài)組件上使用 keep-alive
我們之前曾經(jīng)在一個(gè)多標(biāo)簽的界面中使用 is 特性來(lái)切換不同的組件:
<component v-bind:is="currentTabComponent"></component>
當(dāng)在這些組件之間切換的時(shí)候,你有時(shí)會(huì)想保持這些組件的狀態(tài),以避免反復(fù)重渲染導(dǎo)致的性能問(wèn)題。
重新創(chuàng)建動(dòng)態(tài)組件的行為通常是非常有用的,但是在這個(gè)案例中,我們更希望那些標(biāo)簽的組件實(shí)例能夠被在它們第一次被創(chuàng)建的時(shí)候緩存下來(lái)。為了解決這個(gè)問(wèn)題,我們可以用一個(gè) <keep-alive> 元素將其動(dòng)態(tài)組件包裹起來(lái)。
<!-- 失活的組件將會(huì)被緩存!--><keep-alive>
?<component v-bind:is="currentTabComponent"></component></keep-alive>
修改后的結(jié)果,組件的狀態(tài)可以被緩存
異步組件
在大型應(yīng)用中,我們可能需要將應(yīng)用分割成小一些的代碼塊,并且只在需要的時(shí)候才從服務(wù)器加載一個(gè)模塊。為了簡(jiǎn)化,Vue 允許你以一個(gè)工廠(chǎng)函數(shù)的方式定義你的組件,這個(gè)工廠(chǎng)函數(shù)會(huì)異步解析你的組件定義。Vue 只有在這個(gè)組件需要被渲染的時(shí)候才會(huì)觸發(fā)該工廠(chǎng)函數(shù),且會(huì)把結(jié)果緩存起來(lái)供未來(lái)重渲染。例如:
Vue.component('async-example', function (resolve, reject) {
?setTimeout(function () {
? ?// 向 `resolve` 回調(diào)傳遞組件定義
? ?resolve({
? ? ?template: '<div>I am async!</div>'
? ?})
?}, 1000)
})
如你所見(jiàn),這個(gè)工廠(chǎng)函數(shù)會(huì)收到一個(gè) resolve 回調(diào),這個(gè)回調(diào)函數(shù)會(huì)在你從服務(wù)器得到組件定義的時(shí)候被調(diào)用。你也可以調(diào)用 reject(reason) 來(lái)表示加載失敗。這里的 setTimeout 是為了演示用的,如何獲取組件取決于你自己。一個(gè)推薦的做法是將異步組件和 webpack 的 code-splitting 功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) {
?// 這個(gè)特殊的 `require` 語(yǔ)法將會(huì)告訴 webpack
?// 自動(dòng)將你的構(gòu)建代碼切割成多個(gè)包,這些包
?// 會(huì)通過(guò) Ajax 請(qǐng)求加載
?require(['./my-async-component'], resolve)
})
你也可以在工廠(chǎng)函數(shù)中返回一個(gè) Promise,所以把 webpack 2 和 ES2015 語(yǔ)法加在一起,我們可以寫(xiě)成這樣:
Vue.component(
?'async-webpack-example',
?// 這個(gè) `import` 函數(shù)會(huì)返回一個(gè) `Promise` 對(duì)象。
?() => import('./my-async-component')
)
當(dāng)使用局部注冊(cè)的時(shí)候,你也可以直接提供一個(gè)返回 Promise 的函數(shù):
new Vue({
?// ...
?components: {
? ?'my-component': () => import('./my-async-component')
?}
})
處理加載狀態(tài)
這里的異步組件工廠(chǎng)函數(shù)也可以返回一個(gè)如下格式的對(duì)象:

第二節(jié) 組件基礎(chǔ)與生命周期
處理邊界情況
訪(fǎng)問(wèn)元素 & 組件
在絕大多數(shù)情況下,我們最好不要觸達(dá)另一個(gè)組件實(shí)例內(nèi)部或手動(dòng)操作 DOM 元素。不過(guò)也確實(shí)在一些情況下做這些事情是合適的。
訪(fǎng)問(wèn)根實(shí)例
在每個(gè) new Vue 實(shí)例的子組件中,其根實(shí)例可以通過(guò) $root 屬性進(jìn)行訪(fǎng)問(wèn)。例如,在這個(gè)根實(shí)例中:

所有的子組件都可以將這個(gè)實(shí)例作為一個(gè)全局 store 來(lái)訪(fǎng)問(wèn)或使用。

訪(fǎng)問(wèn)父級(jí)組件實(shí)例
和 $root 類(lèi)似,$parent 屬性可以用來(lái)從一個(gè)子組件訪(fǎng)問(wèn)父組件的實(shí)例。它提供了一種機(jī)會(huì),可以在后期隨時(shí)觸達(dá)父級(jí)組件,以替代將數(shù)據(jù)以 prop 的方式傳入子組件的方式。
另外在一些可能適當(dāng)?shù)臅r(shí)候,你需要特別地共享一些組件庫(kù)。舉個(gè)例子,在和 JavaScript API 進(jìn)行交互而不渲染 HTML 的抽象組件內(nèi),諸如這些假設(shè)性的 Google 地圖組件一樣:
<google-map>
?<google-map-markers v-bind:places="iceCreamShops"></google-map-markers></google-map>
這個(gè) <google-map> 組件可以定義一個(gè) map 屬性,所有的子組件都需要訪(fǎng)問(wèn)它。在這種情況下 <google-map-markers> 可能想要通過(guò)類(lèi)似 this.$parent.getMap 的方式訪(fǎng)問(wèn)那個(gè)地圖,以便為其添加一組標(biāo)記。你可以在這里查閱這種模式。
請(qǐng)留意,盡管如此,通過(guò)這種模式構(gòu)建出來(lái)的那個(gè)組件的內(nèi)部仍然是容易出現(xiàn)問(wèn)題的。比如,設(shè)想一下我們添加一個(gè)新的 <google-map-region> 組件,當(dāng) <google-map-markers> 在其內(nèi)部出現(xiàn)的時(shí)候,只會(huì)渲染那個(gè)區(qū)域內(nèi)的標(biāo)記:
<google-map>
?<google-map-region v-bind:shape="cityBoundaries">
? ?<google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
?</google-map-region></google-map>
那么在 <google-map-markers> 內(nèi)部你可能發(fā)現(xiàn)自己需要一些類(lèi)似這樣的 hack:
var map = this.$parent.map || this.$parent.$parent.map
很快它就會(huì)失控。這也是我們針對(duì)需要向任意更深層級(jí)的組件提供上下文信息時(shí)推薦依賴(lài)注入的原因。
訪(fǎng)問(wèn)子組件實(shí)例或子元素
盡管存在 prop 和事件,有的時(shí)候你仍可能需要在 JavaScript 里直接訪(fǎng)問(wèn)一個(gè)子組件。為了達(dá)到這個(gè)目的,你可以通過(guò) ref 特性為這個(gè)子組件賦予一個(gè) ID 引用。例如:
<base-input ref="usernameInput"></base-input>
現(xiàn)在在你已經(jīng)定義了這個(gè) ref 的組件里,你可以使用:
this.$refs.usernameInput
來(lái)訪(fǎng)問(wèn)這個(gè) <base-input> 實(shí)例,以便不時(shí)之需。比如程序化地從一個(gè)父級(jí)組件聚焦這個(gè)輸入框。在剛才那個(gè)例子中,該 <base-input> 組件也可以使用一個(gè)類(lèi)似的 ref 提供對(duì)內(nèi)部這個(gè)指定元素的訪(fǎng)問(wèn),例如:
<input ref="input">
甚至可以通過(guò)其父級(jí)組件定義方法:
methods: {
?// 用來(lái)從父級(jí)組件聚焦輸入框
?focus: function () {
? ?this.$refs.input.focus()
?}
}
這樣就允許父級(jí)組件通過(guò)下面的代碼聚焦 <base-input> 里的輸入框:
this.$refs.usernameInput.focus()
當(dāng) ref 和 v-for 一起使用的時(shí)候,你得到的引用將會(huì)是一個(gè)包含了對(duì)應(yīng)數(shù)據(jù)源的這些子組件的數(shù)組。
組件生命周期
每個(gè) Vue 實(shí)例在被創(chuàng)建時(shí)都要經(jīng)過(guò)一系列的初始化過(guò)程——例如,需要設(shè)置數(shù)據(jù)監(jiān)聽(tīng)、編譯模板、將實(shí)例掛載到 DOM 并在數(shù)據(jù)變化時(shí)更新 DOM 等。同時(shí)在這個(gè)過(guò)程中也會(huì)運(yùn)行一些叫做生命周期鉤子的函數(shù),這給了用戶(hù)在不同階段添加自己的代碼的機(jī)會(huì)。
比如 created 鉤子可以用來(lái)在一個(gè)實(shí)例被創(chuàng)建之后執(zhí)行代碼:

也有一些其它的鉤子,在實(shí)例生命周期的不同階段被調(diào)用,如 mounted、updated 和 destroyed。生命周期鉤子的 this 上下文指向調(diào)用它的 Vue 實(shí)例。
生命周期圖示
下圖展示了實(shí)例的生命周期。你不需要立馬弄明白所有的東西,不過(guò)隨著你的不斷學(xué)習(xí)和使用,它的參考價(jià)值會(huì)越來(lái)越高

組件復(fù)用性
這里我們又回到data函數(shù)狀態(tài)下,data必須是一個(gè)純函數(shù)。
當(dāng)我們定義這個(gè) <button-counter> 組件時(shí),你可能會(huì)發(fā)現(xiàn)它的 data 并不是像這樣直接提供一個(gè)對(duì)象:
data: {
?count: 0
}
取而代之的是,一個(gè)組件的 data 選項(xiàng)必須是一個(gè)函數(shù),因此每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立的拷貝:
data: function () {
?return {
? ?count: 0
?}
}
如果 Vue 沒(méi)有這條規(guī)則,點(diǎn)擊一個(gè)按鈕就可能會(huì)像如下代碼一樣影響到其它所有實(shí)例:
這里可以增加列表性實(shí)例的引用。
第三節(jié) Vue API
過(guò)渡&動(dòng)畫(huà)
Vue 在插入、更新或者移除 DOM 時(shí),提供多種不同方式的應(yīng)用過(guò)渡效果。
包括以下工具:
在 CSS 過(guò)渡和動(dòng)畫(huà)中自動(dòng)應(yīng)用 class
可以配合使用第三方 CSS 動(dòng)畫(huà)庫(kù),如 Animate.css
在過(guò)渡鉤子函數(shù)中使用 JavaScript 直接操作 DOM
可以配合使用第三方 JavaScript 動(dòng)畫(huà)庫(kù),如 Velocity.js
單元素/組件的過(guò)渡
Vue 提供了 transition 的封裝組件,在下列情形中,可以給任何元素和組件添加進(jìn)入/離開(kāi)過(guò)渡
條件渲染 (使用 v-if)
條件展示 (使用 v-show)
動(dòng)態(tài)組件
組件根節(jié)點(diǎn)
這里是一個(gè)典型的例子:

當(dāng)插入或刪除包含在 transition 組件中的元素時(shí),Vue 將會(huì)做以下處理:
自動(dòng)嗅探目標(biāo)元素是否應(yīng)用了 CSS 過(guò)渡或動(dòng)畫(huà),如果是,在恰當(dāng)?shù)臅r(shí)機(jī)添加/刪除 CSS 類(lèi)名。
如果過(guò)渡組件提供了 JavaScript 鉤子函數(shù),這些鉤子函數(shù)將在恰當(dāng)?shù)臅r(shí)機(jī)被調(diào)用。
如果沒(méi)有找到 JavaScript 鉤子并且也沒(méi)有檢測(cè)到 CSS 過(guò)渡/動(dòng)畫(huà),DOM 操作 (插入/刪除) 在下一幀中立即執(zhí)行。(注意:此指瀏覽器逐幀動(dòng)畫(huà)機(jī)制,和 Vue 的 nextTick 概念不同)
過(guò)渡類(lèi)名
在進(jìn)入/離開(kāi)的過(guò)渡中,會(huì)有 6 個(gè) class 切換。
v-enter:定義進(jìn)入過(guò)渡的開(kāi)始狀態(tài)。在元素被插入之前生效,在元素被插入之后的下一幀移除。
v-enter-active:定義進(jìn)入過(guò)渡生效時(shí)的狀態(tài)。在整個(gè)進(jìn)入過(guò)渡的階段中應(yīng)用,在元素被插入之前生效,在過(guò)渡/動(dòng)畫(huà)完成之后移除。這個(gè)類(lèi)可以被用來(lái)定義進(jìn)入過(guò)渡的過(guò)程時(shí)間,延遲和曲線(xiàn)函數(shù)。
v-enter-to: 2.1.8版及以上 定義進(jìn)入過(guò)渡的結(jié)束狀態(tài)。在元素被插入之后下一幀生效 (與此同時(shí) v-enter 被移除),在過(guò)渡/動(dòng)畫(huà)完成之后移除。
v-leave: 定義離開(kāi)過(guò)渡的開(kāi)始狀態(tài)。在離開(kāi)過(guò)渡被觸發(fā)時(shí)立刻生效,下一幀被移除。
v-leave-active:定義離開(kāi)過(guò)渡生效時(shí)的狀態(tài)。在整個(gè)離開(kāi)過(guò)渡的階段中應(yīng)用,在離開(kāi)過(guò)渡被觸發(fā)時(shí)立刻生效,在過(guò)渡/動(dòng)畫(huà)完成之后移除。這個(gè)類(lèi)可以被用來(lái)定義離開(kāi)過(guò)渡的過(guò)程時(shí)間,延遲和曲線(xiàn)函數(shù)。
v-leave-to: 2.1.8版及以上 定義離開(kāi)過(guò)渡的結(jié)束狀態(tài)。在離開(kāi)過(guò)渡被觸發(fā)之后下一幀生效 (與此同時(shí) v-leave 被刪除),在過(guò)渡/動(dòng)畫(huà)完成之后移除。

對(duì)于這些在過(guò)渡中切換的類(lèi)名來(lái)說(shuō),如果你使用一個(gè)沒(méi)有名字的 <transition>,則 v- 是這些類(lèi)名的默認(rèn)前綴。如果你使用了 <transition name="my-transition">,那么 v-enter 會(huì)替換為 my-transition-enter。
v-enter-active 和 v-leave-active 可以控制進(jìn)入/離開(kāi)過(guò)渡的不同的緩和曲線(xiàn),在下面章節(jié)會(huì)有個(gè)示例說(shuō)明。
CSS 過(guò)渡
常用的過(guò)渡都是使用 CSS 過(guò)渡。
下面是一個(gè)簡(jiǎn)單例子:

CSS 動(dòng)畫(huà)
CSS 動(dòng)畫(huà)用法同 CSS 過(guò)渡,區(qū)別是在動(dòng)畫(huà)中 v-enter 類(lèi)名在節(jié)點(diǎn)插入 DOM 后不會(huì)立即刪除,而是在 animationend 事件觸發(fā)時(shí)刪除。

自定義過(guò)渡的類(lèi)名
我們可以通過(guò)以下特性來(lái)自定義過(guò)渡類(lèi)名:
enter-class
enter-active-class
enter-to-class (2.1.8+)
leave-class
leave-active-class
leave-to-class (2.1.8+)
他們的優(yōu)先級(jí)高于普通的類(lèi)名,這對(duì)于 Vue 的過(guò)渡系統(tǒng)和其他第三方 CSS 動(dòng)畫(huà)庫(kù),如 Animate.css 結(jié)合使用十分有用。

自定義指令
除了核心功能默認(rèn)內(nèi)置的指令 (v-model 和 v-show),Vue 也允許注冊(cè)自定義指令。注意,在 Vue2.0 中,代碼復(fù)用和抽象的主要形式是組件。然而,有的情況下,你仍然需要對(duì)普通 DOM 元素進(jìn)行底層操作,這時(shí)候就會(huì)用到自定義指令。舉個(gè)聚焦輸入框的例子
當(dāng)頁(yè)面加載時(shí),該元素將獲得焦點(diǎn) (注意:autofocus 在移動(dòng)版 Safari 上不工作)。事實(shí)上,只要你在打開(kāi)這個(gè)頁(yè)面后還沒(méi)點(diǎn)擊過(guò)任何內(nèi)容,這個(gè)輸入框就應(yīng)當(dāng)還是處于聚焦?fàn)顟B(tài)?,F(xiàn)在讓我們用指令來(lái)實(shí)現(xiàn)這個(gè)功能:
// 注冊(cè)一個(gè)全局自定義指令 `v-focus`
Vue.directive('focus', {
?// 當(dāng)被綁定的元素插入到 DOM 中時(shí)……
?inserted: function (el) {
? ?// 聚焦元素
? ?el.focus()
?}
})
如果想注冊(cè)局部指令,組件中也接受一個(gè) directives 的選項(xiàng):
directives: {
?focus: {
? ?// 指令的定義
? ?inserted: function (el) {
? ? ?el.focus()
? ?}
?}
}
然后你可以在模板中任何元素上使用新的 v-focus 屬性,如下:
<input v-focus>
鉤子函數(shù)
一個(gè)指令定義對(duì)象可以提供如下幾個(gè)鉤子函數(shù) (均為可選):
bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。
inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用 (僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。
update:所在組件的 VNode 更新時(shí)調(diào)用,但是可能發(fā)生在其子 VNode 更新之前。指令的值可能發(fā)生了改變,也可能沒(méi)有。但是你可以通過(guò)比較更新前后的值來(lái)忽略不必要的模板更新 (詳細(xì)的鉤子函數(shù)參數(shù)見(jiàn)下)。
componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用。
unbind:只調(diào)用一次,指令與元素解綁時(shí)調(diào)用。
接下來(lái)我們來(lái)看一下鉤子函數(shù)的參數(shù) (即 el、binding、vnode 和 oldVnode)。
鉤子函數(shù)參數(shù)
指令鉤子函數(shù)會(huì)被傳入以下參數(shù):
el:指令所綁定的元素,可以用來(lái)直接操作 DOM 。
binding:一個(gè)對(duì)象,包含以下屬性:
name:指令名,不包括 v- 前綴。
value:指令的綁定值,例如:v-my-directive="1 + 1" 中,綁定值為 2。
oldValue:指令綁定的前一個(gè)值,僅在 update 和 componentUpdated 鉤子中可用。無(wú)論值是否改變都可用。
expression:字符串形式的指令表達(dá)式。例如 v-my-directive="1 + 1" 中,表達(dá)式為 "1 + 1"。
arg:傳給指令的參數(shù),可選。例如 v-my-directive:foo 中,參數(shù)為 "foo"。
modifiers:一個(gè)包含修飾符的對(duì)象。例如:v-my-directive.foo.bar 中,修飾符對(duì)象為 { foo: true, bar: true }。
vnode:Vue 編譯生成的虛擬節(jié)點(diǎn)。移步 VNode API 來(lái)了解更多詳情。
oldVnode:上一個(gè)虛擬節(jié)點(diǎn),僅在 update 和 componentUpdated 鉤子中可用。
過(guò)濾器
Vue.js 允許你自定義過(guò)濾器,可被用于一些常見(jiàn)的文本格式化。過(guò)濾器可以用在兩個(gè)地方:雙花括號(hào)插值和 v-bind 表達(dá)式 (后者從 2.1.0+ 開(kāi)始支持)。過(guò)濾器應(yīng)該被添加在 JavaScript 表達(dá)式的尾部,由“管道”符號(hào)指示:
<!-- 在雙花括號(hào)中 -->{{ message | capitalize }}<!-- 在 `v-bind` 中 --><div v-bind:id="rawId | formatId"></div>
你可以在一個(gè)組件的選項(xiàng)中定義本地的過(guò)濾器:
filters: {
?capitalize: function (value) {
? ?if (!value) return ''
? ?value = value.toString()
? ?return value.charAt(0).toUpperCase() + value.slice(1)
?}
}
或者在創(chuàng)建 Vue 實(shí)例之前全局定義過(guò)濾器:

當(dāng)全局過(guò)濾器和局部過(guò)濾器重名時(shí),會(huì)采用局部過(guò)濾器。
過(guò)濾器函數(shù)總接收表達(dá)式的值 (之前的操作鏈的結(jié)果) 作為第一個(gè)參數(shù)。在上述例子中,capitalize 過(guò)濾器函數(shù)將會(huì)收到 message 的值作為第一個(gè)參數(shù)。
過(guò)濾器可以串聯(lián):
{{ message | filterA | filterB }}
在這個(gè)例子中,filterA 被定義為接收單個(gè)參數(shù)的過(guò)濾器函數(shù),表達(dá)式 message 的值將作為參數(shù)傳入到函數(shù)中。然后繼續(xù)調(diào)用同樣被定義為接收單個(gè)參數(shù)的過(guò)濾器函數(shù) filterB,將 filterA 的結(jié)果傳遞到 filterB 中。
過(guò)濾器是 JavaScript 函數(shù),因此可以接收參數(shù):
{{ message | filterA('arg1', arg2) }}
這里,filterA 被定義為接收三個(gè)參數(shù)的過(guò)濾器函數(shù)。其中 message 的值作為第一個(gè)參數(shù),普通字符串 'arg1' 作為第二個(gè)參數(shù),表達(dá)式 arg2 的值作為第三個(gè)參數(shù)。
渲染函數(shù)&JSX
Vue 推薦在絕大多數(shù)情況下使用模板來(lái)創(chuàng)建你的 HTML。然而在一些場(chǎng)景中,你真的需要 JavaScript 的完全編程的能力。這時(shí)你可以用渲染函數(shù),它比模板更接近編譯器。
這里參考React,不再贅述