Vue.js 是一套構(gòu)建用戶界面的漸進式框架。與其他重量級框架不同的是,Vue 采用自底向上增量開發(fā)的設(shè)計。Vue 的核心庫只關(guān)注視圖層,它不僅易于上手,還便于與第三方庫或既有項目整合。另一方面,當(dāng)與單文件組件和 Vue 生態(tài)系統(tǒng)支持的庫結(jié)合使用時,Vue 也完全能夠為復(fù)雜的單頁應(yīng)用程序提供驅(qū)動。
構(gòu)造器
每個 Vue.js 應(yīng)用都是通過構(gòu)造函數(shù)Vue創(chuàng)建一個Vue 的根實例啟動的:
var vm = new Vue({
// 選項
})
在實例化 Vue 時,需要傳入一個選項對象,它可以包含數(shù)據(jù)、模板、掛載元素、方法、生命周期鉤子等選項。
可以擴展Vue構(gòu)造器,從而用預(yù)定義選項創(chuàng)建可復(fù)用的組件構(gòu)造器:
var MyComponent = Vue.extend({
// 擴展選項
})
// 所有的 `MyComponent` 實例都將以預(yù)定義的擴展選項被創(chuàng)建
var myComponentInstance = new MyComponent()
屬性與方法
每個 Vue 實例都會代理其data對象里所有的屬性:
var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> true
// 設(shè)置屬性也會影響到原始數(shù)據(jù)
vm.a = 2
data.a // -> 2
// ... 反之亦然
data.a = 3
vm.a // -> 3
注意只有這些被代理的屬性是響應(yīng)的。如果在實例創(chuàng)建之后添加新的屬性到實例上,它不會觸發(fā)視圖更新。
除了 data 屬性, Vue 實例暴露了一些有用的實例屬性與方法。這些屬性與方法都有前綴$,以便與代理的 data 屬性區(qū)分。例如:
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // -> true
vm.$el === document.getElementById('example') // -> true
// $watch 是一個實例方法
vm.$watch('a', function (newVal, oldVal) {
// 這個回調(diào)將在 `vm.a` 改變后調(diào)用
})
注意,不要在實例屬性或者回調(diào)函數(shù)中(如vm.$watch('a', newVal => this.myMethod()))使用箭頭函數(shù)。因為箭頭函數(shù)綁定父級上下文,所以 this 不會像預(yù)想的一樣是Vue實例,而是this.myMethod未被定義。
下圖說明了實例的生命周期:

下面我們來看下語法:
1.模板語法
Vue.js 使用了基于 HTML 的模板語法,允許開發(fā)者聲明式地將 DOM 綁定至底層 Vue 實例的數(shù)據(jù)。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循規(guī)范的瀏覽器和 HTML 解析器解析。
文本
數(shù)據(jù)綁定最常見的形式就是使用 “Mustache” 語法(雙大括號)的文本插值:
<span>Message: {{ msg }}</span>
Mustache 標(biāo)簽將會被替代為對應(yīng)數(shù)據(jù)對象上 msg 屬性的值。無論何時,綁定的數(shù)據(jù)對象上 msg 屬性發(fā)生了改變,插值處的內(nèi)容都會更新。
通過使用v-once指令,你也能執(zhí)行一次性地插值,當(dāng)數(shù)據(jù)改變時,插值處的內(nèi)容不會更新。但請留心這會影響到該節(jié)點上所有的數(shù)據(jù)綁定:
<span v-once>This will never change: {{ msg }}</span>
純 HTML
雙大括號會將數(shù)據(jù)解釋為純文本,而非 HTML 。為了輸出真正的 HTML ,你需要使用v-html指令:
<div v-html="rawHtml"></div>
這個 div 的內(nèi)容將會被替換成為屬性值rawHtml,直接作為 HTML —— 數(shù)據(jù)綁定會被忽略。
屬性
Mustache 不能在 HTML 屬性中使用,應(yīng)使用 v-bind 指令:
<div v-bind:id="dynamicId"></div>
這對布爾值的屬性也有效 —— 如果條件被求值為 false 的話該屬性會被移除:
<button v-bind:disabled="isButtonDisabled">Button</button>
使用 JavaScript 表達式
迄今為止,在我們的模板中,我們一直都只綁定簡單的屬性鍵值。但實際上,對于所有的數(shù)據(jù)綁定, Vue.js 都提供了完全的 JavaScript 表達式支持。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
2.指令
指令(Directives)是帶有v-前綴的特殊屬性。指令屬性的值預(yù)期是單一 JavaScript 表達式(除了 v-for)。指令的職責(zé)就是當(dāng)其表達式的值改變時相應(yīng)地將某些行為應(yīng)用到 DOM 上。舉個??:
<p v-if="seen">Now you see me</p>
這里,v-if指令將根據(jù)表達式 seen 的值的真假來移除/插入 <p> 元素。
參數(shù)
一些指令能接受一個“參數(shù)”,在指令后以冒號指明。例如,v-bind指令被用來響應(yīng)地更新 HTML 屬性:
<a v-bind:href="url"></a>
在這里href是參數(shù),告知v-bind指令將該元素的href屬性與表達式url的值綁定。
另一個例子是v-on指令,它用于監(jiān)聽DOM事件:
<a v-on:click="doSomething">
在這里參數(shù)是監(jiān)聽的事件名。
修飾符
修飾符(Modifiers)是以半角句號.指明的特殊后綴,用于指出一個指令應(yīng)該以特殊方式綁定。例如,.prevent修飾符告訴v-on指令對于觸發(fā)的事件調(diào)用 event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
3.過濾器
Vue.js 允許你自定義過濾器,可被用作一些常見的文本格式化。過濾器可以用在兩個地方:mustache 插值和v-bind表達式。過濾器應(yīng)該被添加在 JavaScript 表達式的尾部,由“管道”符指示:
<!-- in mustaches -->
{{ message | capitalize }}
<!-- in v-bind -->
<div v-bind:id="rawId | formatId"></div>
4.縮寫
v-前綴在模板中是作為一個標(biāo)示 Vue 特殊屬性的明顯標(biāo)識。當(dāng)你使用 Vue.js 為現(xiàn)有的標(biāo)記添加動態(tài)行為時,它會很有用,但對于一些經(jīng)常使用的指令來說有點繁瑣。同時,當(dāng)搭建 Vue.js 管理所有模板的 SPA 時,v- 前綴也變得沒那么重要了。因此,Vue.js 為兩個最為常用的指令提供了特別的縮寫:
v-bind縮寫
<!-- 完整語法 -->
<a v-bind:href="url"></a>
<!-- 縮寫 -->
<a :href="url"></a>
v-on縮寫
<!-- 完整語法 -->
<a v-on:click="doSomething"></a>
<!-- 縮寫 -->
<a @click="doSomething"></a>
計算屬性
計算屬性 vs Methods
<p>Reversed message: "{{ reversedMessage() }}"</p>
// in component
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我們可以將同一函數(shù)定義為一個 method 而不是一個計算屬性。對于最終的結(jié)果,兩種方式確實是相同的。然而,不同的是計算屬性是基于它們的依賴進行緩存的。計算屬性只有在它的相關(guān)依賴發(fā)生改變時才會重新求值。這就意味著只要 message 還沒有發(fā)生改變,多次訪問 reversedMessage 計算屬性會立即返回之前的計算結(jié)果,而不必再次執(zhí)行函數(shù)
觀察 Watchers
雖然計算屬性在大多數(shù)情況下更合適,但有時也需要一個自定義的watcher。這是為什么 Vue 提供一個更通用的方法通過watch選項,來響應(yīng)數(shù)據(jù)的變化。當(dāng)你想要在數(shù)據(jù)變化響應(yīng)時,執(zhí)行異步操作或開銷較大的操作,這是很有用的。
例如:
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- Since there is already a rich ecosystem of ajax libraries -->
<!-- and collections of general-purpose utility methods, Vue core -->
<!-- is able to remain small by not reinventing them. This also -->
<!-- gives you the freedom to just use what you're familiar with. -->
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 question 發(fā)生改變,這個函數(shù)就會運行
question: function (newQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.getAnswer()
}
},
methods: {
// _.debounce 是一個通過 lodash 限制操作頻率的函數(shù)。
// 在這個例子中,我們希望限制訪問yesno.wtf/api的頻率
// ajax請求直到用戶輸入完畢才會發(fā)出
// 學(xué)習(xí)更多關(guān)于 _.debounce function (and its cousin
// _.throttle), 參考: https://lodash.com/docs#debounce
getAnswer: _.debounce(
function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
},
// 這是我們?yōu)橛脩敉V馆斎氲却暮撩霐?shù)
500
)
}
})
</script>
Class 與 Style 綁定
綁定 HTML Class
對象語法
我們可以傳給v-bind:class一個對象,以動態(tài)地切換 class 。
<div v-bind:class="{ active: isActive }"></div>
上面的語法表示 classactive的更新將取決于數(shù)據(jù)屬性isActive是否為真值 。
我們也可以在對象中傳入更多屬性用來動態(tài)切換多個 class 。此外,v-bind:class指令可以與普通的 class 屬性共存。如下模板:
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
如下 data:
data: {
isActive: true,
hasError: false
}
渲染為:
<div class="static active"></div>
當(dāng)isActive或者hasError變化時,class 列表將相應(yīng)地更新。例如,如果hasError的值為true, class列表將變?yōu)?code>"static active text-danger"。
你也可以直接綁定數(shù)據(jù)里的一個對象:
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
渲染的結(jié)果和上面一樣。我們也可以在這里綁定返回對象的計算屬性。這是一個常用且強大的模式:
<div v-bind:class="classObject"></div>
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal',
}
}
}
數(shù)組語法
我們可以把一個數(shù)組傳給 v-bind:class ,以應(yīng)用一個 class 列表:
<div v-bind:class="[activeClass, errorClass]">
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
渲染為:
<div class="active text-danger"></div>
如果你也想根據(jù)條件切換列表中的 class ,可以用三元表達式:
<div v-bind:class="[isActive ? activeClass : '', errorClass]">
不過,當(dāng)有多個條件 class 時這樣寫有些繁瑣??梢栽跀?shù)組語法中使用對象語法:
<div v-bind:class="[{ active: isActive }, errorClass]">
綁定內(nèi)聯(lián)樣式
對象語法
v-bind:style的對象語法十分直觀——看著非常像 CSS ,其實它是一個 JavaScript 對象。 CSS 屬性名可以用駝峰式 (camelCase) 或 (配合引號的) 短橫分隔命名 (kebab-case):
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
數(shù)組語法
v-bind:style的數(shù)組語法可以將多個樣式對象應(yīng)用到一個元素上:
<div v-bind:style="[baseStyles, overridingStyles]">
自動添加前綴
當(dāng)v-bind:style使用需要特定前綴的 CSS 屬性時,如transform,Vue.js 會自動偵測并添加相應(yīng)的前綴。
條件渲染
v-if指令實現(xiàn)條件塊:
<h1 v-if="ok">Yes</h1>
也可以用v-else添加一個 “else” 塊:
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
v-else-if
2.1.0 新增
v-else-if,顧名思義,充當(dāng)v-if的“else-if 塊”??梢枣?zhǔn)降厥褂枚啻危?/p>
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
類似于v-else,v-else-if必須緊跟在v-if或者v-else-if元素之后。
v-show
另一個用于根據(jù)條件展示元素的選項是v-show指令。用法大致一樣:
<h1 v-show="ok">Hello!</h1>
不同的是帶有 v-show 的元素始終會被渲染并保留在 DOM 中。v-show是簡單地切換元素的 CSS 屬性 display 。
列表渲染
v-for
我們用v-for指令根據(jù)一組數(shù)組的選項列表進行渲染。v-for指令需要以item in items形式的特殊語法,items是源數(shù)據(jù)數(shù)組并且item是數(shù)組元素迭代的別名。
基本用法
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{message: 'Foo' },
{message: 'Bar' }
]
}
})
結(jié)果:
Foo
Bar
key
當(dāng) Vue.js 用v-for正在更新已渲染過的元素列表時,它默認(rèn)用 “就地復(fù)用” 策略。如果數(shù)據(jù)項的順序被改變,Vue將不是移動 DOM 元素來匹配數(shù)據(jù)項的順序, 而是簡單復(fù)用此處每個元素,并且確保它在特定索引下顯示已被渲染過的每個元素。這個類似 Vue 1.x 的 track-by="$index" 。
建議盡可能使用v-for來提供key,除非迭代 DOM 內(nèi)容足夠簡單,或者你是故意要依賴于默認(rèn)行為來獲得性能提升。
事件處理器
監(jiān)聽事件
可以用v-on指令監(jiān)聽 DOM 事件來觸發(fā)一些 JavaScript 代碼。
示例:
<div id="example-1">
<button v-on:click="counter += 1">增加 1</button>
<p>這個按鈕被點擊了 {{ counter }} 次。</p>
</div>
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
方法事件處理器
許多事件處理的邏輯都很復(fù)雜,所以直接把 JavaScript 代碼寫在v-on指令中是不可行的。因此v-on可以接收一個定義的方法來調(diào)用。
示例:
<div id="example-2">
<!-- `greet` 是在下面定義的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 對象中定義方法
methods: {
greet: function (event) {
// `this` 在方法里指當(dāng)前 Vue 實例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
// 也可以用 JavaScript 直接調(diào)用方法
example2.greet() // -> 'Hello Vue.js!'
<!-- 阻止單擊事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修飾符可以串聯(lián) -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件偵聽器時使用事件捕獲模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只當(dāng)事件在該元素本身(比如不是子元素)觸發(fā)時觸發(fā)回調(diào) -->
<div v-on:click.self="doThat">...</div>
你可以用v-model指令在表單控件元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會根據(jù)控件類型自動選取正確的方法來更新元素。盡管有些神奇,但v-model本質(zhì)上不過是語法糖,它負責(zé)監(jiān)聽用戶的輸入事件以更新數(shù)據(jù),并特別處理一些極端的例子。
使用組件
注冊
之前說過,我們可以通過以下方式創(chuàng)建一個 Vue 實例:
new Vue({
el: '#some-element',
// 選項
})
要注冊一個全局組件,你可以使用Vue.component(tagName, options)。例如:
Vue.component('my-component', {
// 選項
})
組件在注冊之后,便可以在父實例的模塊中以自定義元素<my-component></my-component>的形式使用。要確保在初始化根實例之前注冊了組件:
<div id="example">
<my-component></my-component>
</div>
// 注冊
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 創(chuàng)建根實例
new Vue({
el: '#example'
})
渲染為:
<div id="example">
<div>A custom component!</div>
</div>
局部注冊
不必在全局注冊每個組件。通過使用組件實例選項注冊,可以使組件僅在另一個實例/組件的作用域中可用:
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 將只在父模板可用
'my-component': Child
}
})
構(gòu)成組件
組件意味著協(xié)同工作,通常父子組件會是這樣的關(guān)系:組件 A 在它的模版中使用了組件 B。它們之間必然需要相互通信:父組件要給子組件傳遞數(shù)據(jù),子組件需要將它內(nèi)部發(fā)生的事情告知給父組件。然而,在一個良好定義的接口中盡可能將父子組件解耦是很重要的。這保證了每個組件可以在相對隔離的環(huán)境中書寫和理解,也大幅提高了組件的可維護性和可重用性。
在 Vue 中,父子組件的關(guān)系可以總結(jié)為 props down, events up。父組件通過 props 向下傳遞數(shù)據(jù)給子組件,子組件通過 events 給父組件發(fā)送消息??纯此鼈兪窃趺垂ぷ鞯?。
