
2015-08-13 |&δ±nbsp; 發布者:梁國(guó)芳&πnbsp; | 查看δ♥(kàn):3320次
IT新聞2013年(nián)末,我還(hái)在Google ∏•βΩCreative Lab工(gōng)作(zuò)。當時(shí)在♦★項目中使用(yòng)了(le)一(yī)段時(↕×shí)間(jiān)的(de)Angular★®',在感歎數(shù)據綁定帶來(lái)生(shēng)産力提÷↔λ升的(de)同時(shí),我也(yě)感到(dδ₩✔ào)Angular的(de)API設計(jì)過于繁瑣,使得(de)學習§φ(xí)曲線頗為(wèi)陡峭。出于對(duì)AngulaΩ•♦r數(shù)據綁定原理(lǐ)的(de)好(hǎo)奇,我開(kāi)始 δδ“造輪子(zǐ)”,自(zì)己實現(÷≠₹∏xiàn)了(le)一(yī)個(gè)非常粗糙的(de)、π<≥±基于依賴收集的(de)數(shù)據綁定庫。這(zhè)就(jiù)是(sh÷↓εì)Vue.js的(de)前身(shēn)。同時≤±(shí)在實際開(kāi)發中,我發現(xiàn)用≥→→δ(yòng)戶界面完全可(kě)以用(yò©↓↑βng)嵌套的(de)組件(jiàn)樹(shù)來(lái)描述,而一(yīγ)個(gè)組件(jiàn)恰恰可(kě)以對(duì)應¶✘♣"MVVM中的(de)ViewModel。于是(s₩→ hì)我決定将我的(de)數(shù)據綁定實驗改進成 β<一(yī)個(gè)真正的(de)開(kāi)源 項目,其核心思想便是(shì) “數(shù)據驅動的(de)組εφ×♥件(jiàn)系統”。
MVVM的(de)本質是(shì)通(tōng)過數(shù)據綁定鏈接Vi&••☆ew和(hé)Model,讓數(shù)據的(de"→)變化(huà)自(zì)動映射為(wèi)視(shì)圖∞☆的(de)更新。Vue.js在數(shù)據綁定≥↓±✔的(de)API設計(jì)上(shàng)借鑒了(le)Angular的(≠∏♥de)指令機(jī)制(zhì):用(yò←§$±ng)戶可(kě)以通(tōng)過具有(yǒu)特殊前綴的(de)HTML Ωβ↕屬性來(lái)實現(xiàn)數(shù)據綁定,也(yě)可 ∏(kě)以使用(yòng)常見(jiàn)的(de)花(huā)括号模闆插值♣←Ω,或是(shì)在表單元素上(shàng)使用(yòng)雙向綁定:
插值本質上(shàng)也(yě)是(shì)指令,隻是(shì)為(wè>₽λ♣i)了(le)方便模闆的(de)書(shū)寫。在模闆↑±的(de)編譯過程中,Vue.js會(huì)為(wèi)每₽× 一(yī)處需要(yào)動态更新的(de)DOM節點創建一(yī)個(÷ gè)指令對(duì)象。每當一(yī)個(gè₽£)指令對(duì)象觀測的(de)數(shù)據變化(huà)時(shí),它π®₩λ便會(huì)對(duì)所綁定的(de)目标節點執行Ω♠↓¥(xíng)相(xiàng)應的(de)DOM操作(zuò)。基于指↕¥令的(de)數(shù)據綁定使得(de)具體(tǐ)的(de)DOβ∞"₽M操作(zuò)都(dōu)被合理(lǐ)地(dì'•↕¥)封裝在指令定義中,業(yè)務代碼隻需要(yào)涉₽÷£及模闆和(hé)對(duì)數(shù)♥φ♥據狀态的(de)操作(zuò)即可(kě),這(β↔zhè)使得(de)應用(yòng)的(de)開(kāi)®≈↓₩發效率和(hé)可(kě)維護性都(dōu)×≈Ω大(dà)大(dà)提升。
與Angular不(bù)同的(de)是 ®(shì),Vue.js的(de)API™↔ 裡(lǐ)并沒有(yǒu)繁雜(zá)的(de)module、contro™ £ller、scope、factory、service等概念,一(yī)切都≈¥γ(dōu)是(shì)以“ViewModel 實例&rdqu♥↔o;為(wèi)基本單位:
渲染結果:
在渲染的(de)同時(shí),Vue.js也(yě)已經完成了♠<(le)數(shù)據的(de)動态綁定:此✘♣γ時(shí)如(rú)果改動data.msg的•(de)值,DOM将自(zì)動更新。是(shì)不(bù)是(s ☆✘✘hì)非常簡單易懂(dǒng)呢(ne)?除此之外(wài),Vu≥∑e.js對(duì)自(zì)定義指令、過濾器(qì)的(de♥↑Ω)API也(yě)做(zuò)了(le)大(dà)幅的(d"→e)簡化(huà),如(rú)果你(nǐ)有(yǒuα₹)Angular的(de)開(kāi)發經驗,上(&←shàng)手會(huì)非常迅速。
Vue.js的(de)數(shù)據觀測實現(xiàn)原理(lǐ)和(héΩ↔≥)Angular有(yǒu)著(zhe)本質φ→的(de)不(bù)同。了(le)解Angular的(de)讀•'(dú)者可(kě)能(néng)知(zhī)道(dào),An≠§&gular的(de)數(shù)據觀測采₽™₩用(yòng)的(de)是(shì)髒檢查(dirty checking)₩ σ↔機(jī)制(zhì)。每一(yī)個(gè)指令都(dōu)會(h'✔ ↓uì)有(yǒu)一(yī)個(gè)對(duì$©δ•)應的(de)用(yòng)來(lái)觀測數(shù)據& ₹的(de)對(duì)象,叫做(zuò)wat>λcher;一(yī)個(gè)作(zuò∑♥)用(yòng)域中會(huì)有(yǒu)很(hěn)多(duō)個(gè♦♦♠§)watcher。每當界面需要(yào)更新時(shí),Angular會•★β(huì)遍曆當前作(zuò)用(yòn✘♣g)域裡(lǐ)的(de)所有(yǒu)waφ★tcher,對(duì)它們一(yī)一(yī)求值,然後和(hé)之前保存₹↑¥ 的(de)舊(jiù)值進行(xíng)比較✔α✔≤。如(rú)果求值的(de)結果變化(huà)了(le),就(jiù)觸發對✘ (duì)應的(de)更新,這(zhè)個(gè)過程叫做(zuò)♠✔αdigest cycle。髒檢查有(yǒu)兩個(g 'è)問(wèn)題:
Vue.js采用(yòng)的(de)則是(shì)基于"•依賴收集的(de)觀測機(jī)制(zhì)。從(α↔cóng)原理(lǐ)上(shàng)來(lái)說(shuō),和(hé)老∏→(lǎo)牌MVVM框架Knockout是(shì)一(yī)樣的(de)↕∏ 。依賴收集的(de)基本原理(lǐ)是(shì):
Vue.js利用(yòng)了(le)ES5的(de)Obj× ect.defineProperty方法,☆≠直接将原生(shēng)數(shù)據對(duì)象的(" ©de)屬性改造為(wèi)getter和(hé)setter,在這(zhè)兩€✘±₽個(gè)函數(shù)內(nèi)部實現(xiàn)依賴✔ ÷的(de)收集和(hé)觸發,而且完美(m♦<ěi)支持嵌套的(de)對(duì)象結構。對(duì)于數(shù)組,則通↓¥Ω(tōng)過包裹數(shù)組的(de)可(kě)變方法(比↓✘£如(rú)push)來(lái)監聽(tīng)數(shù)組的↓ש(de)變化(huà)。這(zhè)使得(de≈δα)操作(zuò)Vue.js的(de)數(shù)據和₹Ω÷∞(hé)操作(zuò)原生(shēng)對(duì)象幾乎沒有(♥☆<☆yǒu)差别[注:在添加/删除屬性,或是(shì)修改數(shù)組特定位置₽"₩元素時(shí),需要(yào)調用(yòng)特定的(de)函數(shù₹∑),如(rú)obj.$add(key, value)才能(nén'πg)觸發更新。這(zhè)是(shì)受ES₽§5的(de)語言特性所限。],數(shù)據操作(∞≈↑zuò)的(de)邏輯更為(wèi)清晰流暢λ✔,和(hé)第三方數(shù)據同步方案的(de)∑↔♣€整合也(yě)更為(wèi)方便。
在大(dà)型的(de)應用(yòng)中,為(wèiβΩ)了(le)分(fēn)工(gōng)、複用(yòn¥ g)和(hé)可(kě)維護性,我們不(bù)可↕•≠₽(kě)避免地(dì)需要(yào)将應用(yòng)抽象為(w<→♦èi)多(duō)個(gè)相(xiàng)對(duì)獨立的(de)模塊。&&>在較為(wèi)傳統的(de)開(kāi)發模式中,我們隻有(yǒu)↕™在考慮複用(yòng)時(shí)才會(huì)将某一≥©(yī)部分(fēn)做(zuò)成組件(jiàn¶☆);但(dàn)實際上(shàng),應用(yònβ≥'γg)類 UI 完全可(kě)以看(kàn)作(zuò)是(shì)全部由組εφλ件(jiàn)樹(shù)構成的(de):
圖3 UI = 組件(jià↓$n)樹(shù)
因此,在Vue.js的(de)設計(jì)中π∞♣↔将組件(jiàn)作(zuò)為(wèi)∏§© 一(yī)個(gè)核心概念。可(kě)以說(sh★>$uō),每一(yī)個(gè)Vue.js應用(yòng)都(dōu)是(✔α™shì)圍繞著(zhe)組件(jiàn)★φ來(lái)開(kāi)發的(de)。
注冊一(yī)個(gè)Vue.js組件(jiàn)十分(fēn)簡單©₹×:
注冊之後即可(kě)在父組件(jiàn)模闆中以自(zì)定©↓↕義元素的(de)形式調用(yòng)一(yī)個(gè)子(zǐ)組件>₽(jiàn):
渲染結果:
Vue.js的(de)組件(jiàn)可(kě)以理(lǐ)解為β§¥(wèi)預先定義好(hǎo)了(le)行(x→÷≤ íng)為(wèi)的(de)ViewMod"≥el類。一(yī)個(gè)組件(jiàn)可(kě)以預定義很(hěn)↓≈✘σ多(duō)選項,但(dàn)最核心的(de)是(shì)以下(xià)幾個(♣≤☆♥gè):
除此之外(wài),同一(yī)顆組件(jiàn)樹(shù)之內(nè↕™'±i)的(de)組件(jiàn)之間(jiān)還(hái)可(k←σ↔δě)以通(tōng)過內(nèi)建的(de)事(shì)件(jiàn♥±±)API來(lái)進行(xíng)通(tōng↑± )信。Vue.js提供了(le)完善的(de)♠≠定義、複用(yòng)和(hé)嵌套組件(™★jiàn)的(de)API,讓開(kāi)發者可(kě)以像搭積木Ω∑≠'(mù)一(yī)樣用(yòng)組件(jiàn)拼出整↔∞♣<個(gè)應用(yòng)的(de)界面。這(zhè)個(g∞€ ₽è)思路(lù)的(de)可(kě)行(xíng)性在Facebooπ™≥™k開(kāi)源的(de)React當中也(yě)得(de)到( •←§dào)了(le)印證。
Vue.js的(de)核心庫隻提供基本的(£<≥de)API,本身(shēn)在如(rú)何組織應用(yòng)的(de)文÷ (wén)件(jiàn)結構上(shàng)并不(bù)做(zuò₩§©)太多(duō)約束。但(dàn)在構建大(dà)型應用(yòn®≥g)時(shí),推薦使用(yòng)Webpa☆↕Ω ck+vue-loader這(zhè)個(& ∞gè)組合以使針對(duì)組件(jiàn)的(de)開(kā∞±®☆i)發更高(gāo)效。
Webpack是(shì)由Tobias Koppers開(kāi)發的 ±(de)一(yī)個(gè)開(kāi)源前端模塊構建工(gōng™↔ε)具。它的(de)基本功能(néng)是(shì)将以模塊★₹格式書(shū)寫的(de)多(duō)個(g≠λ•¶è)JavaScript文(wén)件(jiàn)打包成一(yī)個(gπ×₹è)文(wén)件(jiàn),同時(sh↑'í)支持CommonJS和(hé)AMD格式。但(dàn)讓它與衆β♦α<不(bù)同的(de)是(shì),它提供了(le)強大($λ"≠dà)的(de)loader API來(lái)定義對(duì×↑)不(bù)同文(wén)件(jiàn)格式的(de)預處π¥理(lǐ)邏輯,從(cóng)而讓我們可(kě)以将CSS、模闆,甚至ε ♣是(shì)自(zì)定義的(de)文(wé&σφ¥n)件(jiàn)格式當做(zuò)JavaScrip>&☆γt模塊來(lái)使用(yòng)。Web£βpack 基于loader還(hái)可(kě)以實現(xiàn)大(dà)量♦™高(gāo)級功能(néng),比如(rú)自(zì)動分(fēn€¥)塊打包并按需加載、對(duì)圖片資源引用(yòng)的(de)自(z♦≠ì)動定位、根據圖片大(dà)小(xiǎo)決定是÷>(shì)否用(yòng)base64內(nèi)聯、開(kā÷&i)發時(shí)的(de)模塊熱(rè)替換等等,可(k↕↕≈ě)以說(shuō)是(shì)目前前端構建領✘×✘域最有(yǒu)競争力的(de)解決方案之一(yī)。
我在Webpack的(de)loader API基礎上(shàn ε™g)開(kāi)發了(le)vue-loader插件(jiàn),從(có↓♦♣ng)而讓我們可(kě)以用(yòng)這(zhè)樣≤δγφ的(de)單文(wén)件(jiàn)格式 (*.→•vue) 來(lái)書(shū)寫Vue組件(α∑±♦jiàn):
同時(shí),還(hái)可(kě)以在*.vue文(δ wén)件(jiàn)中使用(yòng)其他(t♦ ↓ā)預處理(lǐ)器(qì),隻需要(yào)安裝對(duì)應的(∑♠♣de)Webpack loader即可(kě):
這(zhè)樣的(de)組件(jiàn)格≤®λ÷式,把一(yī)個(gè)組件(jiàn)的(de)模闆、樣式、邏輯三π↕β要(yào)素整合在同一(yī)個(gè)文(wén)件(jiàn)≈φ€¶中,即方便開(kāi)發,也(yě)方便複用(yòng)±§α±和(hé)維護。另外(wài),Vue.js本身(shēn)支↓≠₽持對(duì)組件(jiàn)的(de)異步加α₩←←載,配合Webpack的(de)分(fēn)塊打包功能(néng),可(↓★εkě)以極其輕松地(dì)實現(xiàn)♦↑組件(jiàn)的(de)異步按需加載。
Vue.js還(hái)有(yǒu)幾個(gè)值得(de)一(yβ≈↑↕ī)提的(de)特性:
對(duì)Web Components有(yǒu)了(le)解的(de)讀✔>(dú)者看(kàn)到(dào)這(zhè)裡(lǐ)可(k™€∏ě)能(néng)會(huì)産生(sh&πφβēng)疑問(wèn):Vue.js的(₽✔de)組件(jiàn)和(hé)Web Component' s的(de)區(qū)别在哪裡(lǐ)呢(neφ∏♥ε)?這(zhè)裡(lǐ)簡要(yào)地(dì)做(zuò)一(yī)下(x↑↕ià)分(fēn)析。
Web Components是(shì)一(Ω₽yī)套底層規範,本身(shēn)并不(bù)帶有(yǒu←✔)數(shù)據綁定、動畫(huà)系統等上(sh¶✔'àng)層功能(néng),因此更合适的(de)比★ ★較對(duì)象可(kě)能(néng)是(shì)Polyφ✔↔mer。Polymer在API和(hé)功能(nén<§∑←g)上(shàng)和(hé)Vue.js比較相(xià₹™→∏ng)似,但(dàn)它對(duì)Web Components的(de)♦₹硬性依賴使得(de)它在浏覽器(qì)支持方面有( ♣→yǒu)一(yī)定的(de)問(wèn)題—&m≈↕¥'dash;在不(bù)支持Web Components'®₩規範的(de)浏覽器(qì)中,需要(yào)加載≤↓÷龐大(dà)的(de)polyfill,Ω不(bù)僅在性能(néng)上(shàng)會(huì)有(yǒu)影δ÷β≥(yǐng)響,并且有(yǒu)些(xiē)功能(néng),比≥如(rú)ShadowDOM,polyf★"λill并沒有(yǒu)辦法完美(měi)支持。同時(shí),Web C× ≥omponents規範本身(shēn)尚$< 未定稿,一(yī)些(xiē)具體(tǐ)設計(jìλ€★)上(shàng)仍存在不(bù)小(xiǎo)♠®ε的(de)分(fēn)歧。相(xiàng)比之下(xià),Vue.j>♥s在支持的(de)浏覽器(qì)中(IE9+)沒有(yǒu)任何依賴。
除此之外(wài),在支持Web Components的(φπde)環境中,我們也(yě)可(kě)以很£₽γφ(hěn)簡單地(dì)利用(yòng)Web Components底層APIσγπ将一(yī)個(gè)Vue.js組件(jiàn)封裝在一γ ₩(yī)個(gè)真正的(de)自(zì)定☆↑>≤義元素中,從(cóng)而實現(xiàn)Vue.js組件(jià&→≠n)和(hé)其他(tā)框架的(de)無縫整合。
在發布之初,Vue.js原本是(shì)著≥≈♠×(zhe)眼于輕量的(de)嵌入式使用(yòng)場(chǎng)景。在今天,"≈♠↑Vue.js也(yě)依然适用(yòng)于這(zhè)樣的(de)場(chǎng)景。由于其輕量(22kb min+λ∑αgzip)、高(gāo)性能(néng)的(de)特點,對(du δì)于移動場(chǎng)景也(yě)有(yǒu)很(hěn)∑™好(hǎo)的(de)契合度。更重要(yào)的(de)是(shì)≠®,設計(jì)完備的(de)組件(jiàn)系統和(hé✔♠♣)配套的(de)構建工(gōng)具、插件(jiàn),使得(de)V×§ε±ue.js在保留了(le)其簡潔API的(de)同時(shí),也σ≠≈(yě)已經完全有(yǒu)能(néng§α★↕)力擔當起複雜(zá)的(de)大(dà)型應用(yòn€γ££g)的(de)開(kāi)發。
從(cóng)誕生(shēng)起到(dào)現(xiàn)在的(de)一÷(yī)年(nián)半曆程中,Vue.js經曆了(le)一∞₽(yī)次徹底的(de)重構,多(duō)次API的↔♥™(de)設計(jì)改進,目前已經趨于穩定,測試覆蓋率長(chδ≤¥γáng)期保持在100%,GitHub Bug數(shù)量長(™<cháng)期保持在個(gè)位數(shù δ §),并在世界各地(dì)都(dōu)已經有(yǒu)公司/項目将±±Vue.js應用(yòng)到(dào)±¶生(shēng)産環境中。在2015年(nián∏☆©↓)晚些(xiē)時(shí)候,Vue.js将發布1βε™.0版本,敬請(qǐng)期待。
