鄧成 孫書會
摘要:在應(yīng)用龐大到一定程度時,MVC結(jié)構(gòu)模式也將變得十分復(fù)雜,特別是View的邏輯部分變得難以維護。在這個背景下,MVVM的架構(gòu)模型應(yīng)運而生(model-view-viewmodel)。MVVM的核心是在邏輯代碼中不對View進行直接操作,而是通過ViewModel將View與Model綁定起來,且具有內(nèi)置的互相同步的機制。這種模式解放了開發(fā)者在Model中維護View的大部分工作,對于前端應(yīng)用更是極大地提高了開發(fā)效率和代碼抽象度。前后端分離架構(gòu)的普及以及sPA應(yīng)用的設(shè)計思想都離不開MVVM的流行,這些都帶來了前端技術(shù)的快速發(fā)展。
關(guān)鍵詞:MVVM;代碼質(zhì)量;軟件開發(fā)
中途分類號:TP311 文獻標(biāo)識碼:A
文章編號:1009-3044(2019)29-0249-02
MVVM模式最早的提出并不是在Web前端領(lǐng)域,它最早于2005年被微軟的WPF和Silverlight的架構(gòu)師John Gossman提出,并且應(yīng)用在微軟的軟件開發(fā)中。MVVM的核心概念是實現(xiàn)視圖與模型之間的綁定關(guān)系,這個綁定包括視圖狀態(tài)的處理以及數(shù)據(jù)綁定和數(shù)據(jù)轉(zhuǎn)換,舉例來說視圖中某一處的狀態(tài)與模型層中數(shù)據(jù)綁定在一起,數(shù)據(jù)的變更將會反映到視圖層,這個反應(yīng)機制由VM實現(xiàn)。大部分從MVC到MVVM遷移的開發(fā)者都認(rèn)為MVVM更適合大型應(yīng)用。
1什么是MVVM
MVVM是Model-View-ViewModel的簡稱,即模型一視圖一視圖模型。MVVM的設(shè)計原理是基于MVC的,MVC是Model-View-Controller的簡稱,即模型視圖控制器,如圖1。MVC的設(shè)計思想常被用在服務(wù)端框架上,如SpringMVC。但在前端應(yīng)用上,由于對于View的驅(qū)動過于頻繁且復(fù)雜,MVC并不適合前端業(yè)務(wù)復(fù)雜度高的場景。MVVM則是借鑒MVC的思想改造的設(shè)計模式。
MVVM的最大特點就是開發(fā)者不需要直接改變View,即無須直接操作DOM。開發(fā)者只需要編寫包含聲明綁定的視圖模版,并編寫ViewModel中的數(shù)據(jù)變更邏輯,View將得到響應(yīng)式的改變。這種模式極大地提高了前端的開發(fā)效率和,降低了開發(fā)復(fù)雜度。View的解耦也讓前端代碼的單元測試變得更簡單,提升了前端整體的代碼質(zhì)量。
2MVVM使用現(xiàn)狀
當(dāng)今前端有三大框架:React、Vue、Angular。Angular基于傳統(tǒng)MVC的設(shè)計思想實現(xiàn),React、Vue均是使用MVVM的設(shè)計模式。React和Vue互相借鑒,實現(xiàn)上有很多相似之處。
1)都使用VirtualDOM
2)提供了響應(yīng)式(Reactive)和組件化(Composable)的視圖組件。
3)將注意力集中保持在核心庫,而將其他功能如路由和全局狀態(tài)管理交給相關(guān)的庫。
從框架的API設(shè)計上來看Vue的更簡潔易懂,下文主要參照Vue進行實現(xiàn)。
3實現(xiàn)MVVM設(shè)計模式
從MVVM底層實現(xiàn)人手,實現(xiàn)一個類Vue的簡單MVVM模式。以下內(nèi)容大量參考Vue源碼。Vue源碼地址:https://github.com/vuejs/vue。
MVVM設(shè)計模式主要包括四個部分
模版編譯(Compile)
數(shù)據(jù)劫持(observerl
發(fā)布訂閱(Dep)
觀察者(Watcherl
代碼實現(xiàn)可使用ECMASeript6規(guī)范。首先從開發(fā)者使用角度出發(fā),參照Vue的實現(xiàn),我們需要完成以下功能:
1)模版指令語法的實現(xiàn)如:v-model雙向綁定指令。
2){{Object}}模版對象替換的實現(xiàn)。
3)數(shù)據(jù)變化的監(jiān)聽及視圖的改變。
MVVM是MVVM模式的實現(xiàn)類,開發(fā)者通過使用MVVM類對象完成MVVM框架的調(diào)用。
首先將el、data對象直接復(fù)制在MVVM類上,然后通過模版編譯類渲染頁面。
模版編譯類對開發(fā)者編寫的視圖模版進行編譯,生成真實的DOM結(jié)構(gòu),并在需要時進行模版的重新編譯。
Compile類的構(gòu)造函數(shù)傳人DOM選擇器和MVVM實例。Fragment是DOM節(jié)點的內(nèi)存片段,有著與真實DOM相同的結(jié)構(gòu),使用Fragment操作DOM會更加高效。
遍歷開發(fā)者提供的目標(biāo)節(jié)點所有的子節(jié)點,全部加入frag-ment中,有了DOM的內(nèi)存片段,就可以開始編譯該節(jié)點了。
在模版編譯后,“l(fā)l中的JavaScript表達式將映射為MVVM類中的data對象的某個值。完成這部分工作后,模版的編譯功能已經(jīng)可用了,但是這種編譯只是一個靜態(tài)的編譯機制,在初始編譯一次模版以后,編譯后的內(nèi)容就不可變了。從各個MVVM框架的表現(xiàn)來看,模版與JavaScript之間存在著一種互相影響的關(guān)系,即雙向綁定,再相應(yīng)JavaScript對象的值發(fā)生變化時,我們?nèi)匀恍枰獙⑦@些變化體現(xiàn)在視圖上。這就是MVVM中的數(shù)據(jù)劫持,在ES5中通過Object.defineProperty可以實現(xiàn)對象的劫持,ES6中的Proxy也可以實現(xiàn)。
我們通過寫一個Observer類來實現(xiàn)數(shù)據(jù)劫持:
observe方法可以對傳人的data進行深度遞歸劫持,即對data中的所有嵌套對象都進行劫持操作,以此完成data對象的響應(yīng)化。DefineReactive定義了對象的響應(yīng)變化。
defineProperty中的get和set攔截了對對象的取值和賦值。Dep中定義了MVVM的發(fā)布訂閱模式,在這之前我們需要實現(xiàn)對象的觀察者,完成新值與舊值的對比,如果值發(fā)生了變化才執(zhí)行相應(yīng)的方法。
4MVVM的意義
4.1開發(fā)體驗
視圖與業(yè)務(wù)邏輯的分離極大提升了開發(fā)效率,開發(fā)者不必直接對DOM操作,只需要將精力放在業(yè)務(wù)邏輯的編寫上。
4.2方便測試
在MVC下業(yè)務(wù)邏輯中混合著視圖的操作,基本無法完全測試,在MVVM中只需要測試VM的代碼就可保證業(yè)務(wù)邏輯的正確性。
4.3代碼移植
同一份代碼移植在不同的終端時,只需要更改視圖部分的代碼即可完成兼容。如在ios中使用MVVM模式進行開發(fā)可以使應(yīng)用在iPhone和iPad中簡單地完成代碼的移植。
4.4性能
雖然MVVM并沒有直接影響到前端的性能,但是MVVM模式搭配虛擬DOM技術(shù)可以極大地減少直接的DOM操作。瀏覽器原生的DOM操作極大的占用瀏覽器的性能,對于每次的DOM操作都將重新渲染DOM樹,虛擬DOM通過Diff算法節(jié)省了操作DOM所帶來的開銷。