Vue.js 是一個(gè)漸進(jìn)式 MVVM 框架,目前被廣泛使用,也成為目前前端技術(shù)中頗具代表性的一個(gè)框架。
按 Vue 作者的說(shuō)法,Vue(及其生態(tài))是一個(gè)漸進(jìn)式 MVVM 框架,可以按照實(shí)際需要逐步進(jìn)階使用更多特性。
聲明式渲染
簡(jiǎn)單來(lái)說(shuō),聲明式渲染即希望開(kāi)發(fā)者更多地表達(dá) “想要什么”,而不用太關(guān)心想要的效果的實(shí)現(xiàn)細(xì)節(jié)。例如,我們希望頁(yè)面上有一個(gè) 2x2 的表格,而命令式的實(shí)現(xiàn)方式是使用 Canvas 自己畫(huà):
// 獲取畫(huà)面和context
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 定義寬高和行列數(shù)
const width = 200, height = 100;
const colCount = 2, rowCount = 2;
// todo:定義畫(huà)筆顏色
// 畫(huà)橫線
for(let i = 0; i <= rowCount; i++){
ctx.moveTo(0, height / rowCount * i);
ctx.lineTo(width, height / rowCount * i);
}
// 畫(huà)豎線
for(let i = 0; i <= colCount; i++){
ctx.moveTo(width / colCount * i);
ctx.lineTo(width / colCount * i, height);
}
document.body.appendChild(canvas);
我們需要自己關(guān)心實(shí)現(xiàn)細(xì)節(jié),例如每一條線的坐標(biāo)、每一條表格線的繪制等等。所幸 HTML 為我們提供了 <table>
元素,可以讓我們更簡(jiǎn)單地實(shí)現(xiàn)表格:
<table>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
相比 Canvas 的表格,我們只需要說(shuō)明需要多少行,每一行有多少個(gè)單元格即可,不用自己關(guān)注繪制細(xì)節(jié)。這個(gè)例子可以讓我們對(duì) “聲明式渲染” 有一個(gè)更直觀的認(rèn)知。
而在 Vue 中,開(kāi)發(fā)者可以直接通過(guò)模板指令來(lái)表達(dá) “想要什么”,并不用關(guān)心它的底層實(shí)現(xiàn)。常見(jiàn)的指令如 v-if
、v-for
、v-show
等都是同樣的作用。此外 Vue 還可以聲明式地表達(dá)對(duì)數(shù)據(jù)的渲染邏輯,例如用 {{message}}
來(lái)表示 “在此處顯示變量 message
的值”,而不用關(guān)心 message
的變量是如何被填入 DOM 對(duì)象中。
組件系統(tǒng)
每一個(gè)程序員都知道 “高內(nèi)聚 低耦合” 的編程原則,也都知道代碼復(fù)用的重要性。但在前端代碼中如何實(shí)現(xiàn) “高內(nèi)聚 低耦合”,以及前端代碼復(fù)用,并不是一件容易的事情。對(duì)于 JavaScript,尚可以使用模塊化來(lái)解決內(nèi)聚和復(fù)用的問(wèn)題,但一旦涉及到結(jié)構(gòu)(HTML)和樣式,事情就不容易了。
Vue 為開(kāi)發(fā)者提供了 “組件” 的概念,一個(gè)組件即一組關(guān)聯(lián)的結(jié)構(gòu)和邏輯。組件內(nèi)部可以方便地使用聲明式渲染將邏輯和結(jié)構(gòu)關(guān)聯(lián)起來(lái),實(shí)現(xiàn)組件的高內(nèi)聚。例如 Vue 官方文檔的一個(gè)組件例子:
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
這個(gè)例子中,template
中定義了組件的結(jié)構(gòu),并且聲明式地將 count
變量顯示到 <button>
的內(nèi)容中。在 <button>
被點(diǎn)擊時(shí)改變 count
的值??梢钥吹?,組件內(nèi)部的結(jié)構(gòu)和邏輯都是非常容易互相訪問(wèn)和操作的。而在組件外部,不管是 count
變量還是 <button>
結(jié)構(gòu),都無(wú)法直接進(jìn)行訪問(wèn)和操作。如果需要外部進(jìn)行訪問(wèn)和操作,則需要定義相應(yīng)的接口或者事件。這非常符合 “高內(nèi)聚 低耦合” 的思想。
組件一旦定義好之后即可被重復(fù)使用:
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
從而很好地解決前端代碼復(fù)用的問(wèn)題。
客戶端路由和狀態(tài)管理
針對(duì)單頁(yè)面應(yīng)用,客戶端路由的支持是必不可少的一個(gè)功能。通過(guò)客戶端路由,開(kāi)發(fā)者可以根據(jù)不同的 URL 加載不同的前端組件(有時(shí)候也稱為 “頁(yè)面”),也可以允許用戶通過(guò)跳轉(zhuǎn)的形式在不同的前端組件之間跳轉(zhuǎn)。
要支持客戶端路由,首先需要對(duì)當(dāng)前 URL 進(jìn)行解析,獲取當(dāng)前訪問(wèn)的頁(yè)面地址以及參數(shù)等信息,然后根據(jù)開(kāi)發(fā)者定義的路由與組件的對(duì)應(yīng)關(guān)系(路由表)找到頁(yè)面地址對(duì)應(yīng)的組件,并負(fù)責(zé)加載和運(yùn)行組件。
Vue 官方的客戶端路由被放到一個(gè)獨(dú)立的庫(kù) vue-router
中。開(kāi)發(fā)者也可以根據(jù)需要選用。
隨著應(yīng)用復(fù)雜度的上升,頁(yè)面的數(shù)據(jù)會(huì)越來(lái)越多,且同一個(gè)數(shù)據(jù)關(guān)聯(lián)多個(gè)組件的情況會(huì)越來(lái)越多。此時(shí)數(shù)據(jù)如何組織、如何定義與組件的關(guān)聯(lián)關(guān)系、如何確保變更會(huì)同步到每一個(gè)關(guān)聯(lián)的組件中就變得非常重要。此時(shí)可能就需要使用 “狀態(tài)管理” 的庫(kù)來(lái)輔助開(kāi)發(fā)者進(jìn)行頁(yè)面狀態(tài)的管理。
狀態(tài)管理庫(kù)會(huì)提供全局的狀態(tài)(state
)對(duì)象,并定義一系列讀寫(xiě)狀態(tài)的方法。組件中只要使用狀態(tài)管理提供的讀寫(xiě)方法讀寫(xiě)狀態(tài)即可。
Vue 官方也提供了狀態(tài)管理庫(kù) vuex
,可供復(fù)雜應(yīng)用使用。
編譯 / 構(gòu)建工具
借助構(gòu)建工具的力量,Vue 還可以提供更好的開(kāi)發(fā)體驗(yàn)。例如基于 webpack 強(qiáng)大的 loader 機(jī)制,style-loader
和 css-loader
可以允許開(kāi)發(fā)者在 JS 文件中直接引入 CSS 文件。
借鑒這一思路,Vue 推出了單文件組件(Single File Component,簡(jiǎn)稱 SFC)格式.vue
,即允許開(kāi)發(fā)者將同一組件的結(jié)構(gòu)、樣式、邏輯全部寫(xiě)在同一個(gè)文件中,然后由 vue-loader
在編譯階段將結(jié)構(gòu)和邏輯都編譯成 JS 文件,將 CSS 部分借用上述 style-loader
和 css-loader
同樣的機(jī)制掛載到頁(yè)面中。
單文件組件的推出使得 Vue 的文件組織又上一個(gè)臺(tái)階,我們可以更明確地在文件層面就定義好組件,同時(shí)由于 vue-loader
的支持,組件也可以通過(guò)各種機(jī)制(例如 npm)在不同項(xiàng)目間得以復(fù)用。
除了單文件組件之外,Vue 也推出了 CLI 工具 vue-cli
,方便開(kāi)發(fā)者更好地初始化項(xiàng)目、搭建項(xiàng)目開(kāi)發(fā)環(huán)境并完成構(gòu)建工作。
總結(jié)
通過(guò)上述說(shuō)明,希望大家能更好地理解 “Vue 是一個(gè)漸進(jìn)式的 MVVM 框架” 的含義,它允許我們只用聲明式渲染這一點(diǎn)點(diǎn)特性,也允許我們最后加上客戶端路由和狀態(tài)管理來(lái)支持大型項(xiàng)目。正因?yàn)檫@樣的設(shè)計(jì),使得 Vue 上手也非常容易,不用一次學(xué)習(xí)所有特性的使用。
了解 Vue 漸近式的含義,也可以讓我們?cè)诤罄m(xù)的源碼解讀中能清楚地知道每一章的源碼在 Vue 生態(tài)中的位置。