鄧成 孫書會

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