原文:https://github.com/kuitos/kui...
所有文章:https://github.com/kuitos/kui...前端
原文:The MVVM Patternvue
MVVM 模式跟 Silverlight 這類 XAML 應用平臺是天生合拍的。這是由於 MVVM 模式利用了Silverlight 的一些特殊能力,好比說 數據綁定,命令,行爲等。MVVM 跟其餘一些將表現及UI佈局 與展現層邏輯的職責進行分離的模式很類似;若是你對 MVC 模式熟悉的話,你會發現它與 MVVM 之間存在不少類似的概念。ios
譯者注:XAML(Extensible Application Markup Language)是微軟爲構建GUI程序而建立的一種標記語言,你能夠將它等同於 web 體系中 HTML。如下譯文中,web 開發者能夠將 XAML 統一代入爲 HTML。Silverlight 則是微軟開發的一個 富互聯網應用(Rich Internet Application) 開發平臺。git
諸如 Windows Forms,WPF,Silverlight 以及 Windows Phone 這類開發技術都提供了一個默認的體驗,那就是它可讓開發者從工具箱中拖拽控件到設計面板,而後在代碼文件中編寫必定格式的代碼就能完成整個開發。可是隨着這類應用的增加規模及做用範圍的變化,複雜的維護性問題就會隨之而來。因爲 UI控件 與 業務邏輯 之間的緊耦合,相應帶來的問題就是 UI 變動的代價增大,以及難以編寫針對性的單元測試。github
使用 MVVM 模式來實現應用的主要動機有如下幾點:web
它提供了一系列分離的概念。緊耦合,難變化,脆弱的代碼致使了各類各樣的長期維護的問題,最後致使了客戶對交付的軟件較低的滿意度。在應用邏輯及 UI 之間進行乾淨的分離將會讓應用更容易測試,維護以及拓展。它提升了代碼的重用性同時使得 開發-設計 的工做流變爲可能。數據庫
它與 XAML 平臺是天生合拍的。MVVM 模式的關鍵推進力來自於 Silverlight 平臺豐富的數據綁定技術棧及一些依賴屬性。這些東西的組合提供了 UI 到 VM(譯者注:view model 視圖模型後面統一簡稱 VM) 之間的鏈接方式。編程
它使得 開發-設計 的工做流成爲可能。當 UI XAML 不是與代碼緊耦合時,設計師們就很容易去自由的發揮他們的創造力,從而作出一個更優秀的產品。(譯者:簡單來講就是 視圖層 跟 M/VM 層的開發能夠是正交的。)架構
它加強了應用的可測試性。將 UI 邏輯轉移到一個能夠獨立實例化的類中,可讓單元測試的編寫更加容易。(譯者:這一點其實很是重要)框架
Model-View-ViewModel 的模式能夠用到全部的 XAML 平臺上。它的意圖是在 UI 控件和它們的邏輯之間進行一個純淨的概念分離。
MVVM 模式中有三個核心的組件:model(模型),view(視圖) 以及 view-model(視圖模型)(譯者注:爲保證交流中術語的一致性,model、view、view-model後面均不做翻譯),它們彼此間扮演一個大相徑庭的角色。下面的插圖展現了這三個組件之間的關係:
每一個組件之間都是相互解耦的,這也使得:
組件能夠被交換
內部實現能夠在不影響其餘組件的狀況下修改
組件能夠獨立的運做
隔離的單元測試
除了理解這三個組件的職責,理解組件之間是如何交互的也一樣重要。在最高的層級上,view 能感知到 view-model,view-model 能感知到 model,可是 model 並不會察覺到 view-model 的存在,一樣的,view-model 也察覺不到 view。
view-model 將 model 類 與 view 隔離開來,這也使得 model 能夠獨立於 view 進行演化。
view 的職責是用來定義 結構、佈局,及用戶在屏幕上看到的外觀表現。理想狀況下,view 僅經過 XAML 定義,以及一些有限的不包含業務邏輯的代碼。
在一個 Windows Phone 應用中,view 一般是一個頁面。除此以外,一個 view 也能夠是一個父 view 的子組件,或者一個 ItemsControl 中的 DataTemplate對象。
一個 view 能夠有本身的 view-model,或者繼承自父級 view 的 view-model。視圖經過綁定,或者調用 view-model 上的方法來獲取數據。在運行期間,UI 控件將會響應 view-model 屬性觸發的變化通知,從而改變 view。
有一些 view 上的交互會觸發 view-model 上的代碼執行,好比說按鈕點擊或者選項選中事件。若是這個控件是一個命令源,這個控件的 Command 屬性能夠在 view-model 上綁定成一個 ICommand 屬性。當這個控件的命令被調用,view-model 上相應的代碼就會被執行。除了命令,行爲也能被附加到 view 的一個對象中,而後監聽命令調用及事件觸發。做爲迴應,這個行爲以後能夠調用 view-mode 上的 ICommand 或者方法。(譯者:web 領域裏這些就是指的模板上的事件綁定語法,一般由框架提供)
model 在 MVVM 中是應用的域模型(domain model)實現,它包含數據模型以及相應的業務和校驗邏輯。model 對象的例子包括,數據倉庫(repositories),業務對象,數據轉換對象(DTOs),POCO對象,以及生成的實體及代理對象。
譯者:一個 model 應該包含基本的模型數據、基本的業務邏輯、業務規則、數據轉換、依賴校驗邏輯等。這裏要提到的一個概念就是,域模型分爲兩個類型,一類是貧血的(Anemic domain model),一類是非貧血的(non-anemic)。貧血的域模型只包含基礎的數據信息,而不含有其餘數據校驗、業務規則等邏輯,它更像是一種數據庫結構在代碼層面的還原。在貧血域模型設計中,業務邏輯一般做爲一個獨立的代碼部分,用來轉換模型對象的狀態,且各個處理之間能夠組合嵌套(用過Redux的同窗有沒有以爲很熟悉的感受?)。在面向對象設計領域,這一般被視爲一個反模式。(OO與FP之間的衝突)
view-mode 做爲 view 和 model 的中間人,它的職責是用於處理 view 的邏輯。一般狀況下,view-model 與 model 之間的交互是經過調用 model 類中的方法來完成的。以後 view-model 依據 model 中的數據提供一種方便 view 使用的格式。view-model 從 model 中獲取數據而後使其對 view 可用的同時,爲了讓 view 操做起來更簡單,可能會經過一些方式作數據格式轉換。view-model 還提供了一些命令的實現讓應用的用戶能夠在 view 中使用。好比說,當用戶點擊了 UI 中的一個 button,這個動做能夠觸發 view-model 中的一個命令。view-model 一樣有職責去定義一些某些方面會影響 view 展現的邏輯狀態的變化,好比說一個代表一些操做是掛起的狀態。
爲了讓 view-model 參與到與 view 的雙向數據綁定當中,它的屬性必須觸發 PropertyChanged 事件。
(譯者注:後面這兩段是基於 .NET 平臺的具體代碼實踐,換算到 web 領域,基本上說的就是在 VM 中監聽數據變化,而後作出對應的反應。)
View-model 經過實現 INotifyPropertyChanged 接口以及屬性變化時觸發的 PropertyChanged 事件來知足這個需求。當屬性發生變動時,監聽者能夠適當作迴應。
對於集合而言,相應的提供了視圖友好的工具System.Collections.ObjectModel.ObservableCollection<T> 。這個工具實現了集合變動通知,從而減輕了開發者本身在集合上實現 INotifyCollectionChanged 接口的負擔。
譯者:這裏提到了一個很重要的事情,就是 vm 獲取到 model 中的數據後,可能須要作一些相應的數據格式化,以方便 view 使用。在 ng1.x 及 vue1.x 中,這類操做一般是經過在視圖模板上綁定過濾器語法實現的(
ng-bind="model | upperCase"
),然而 MVVM 定義中,數據轉換/格式化 的操做自己也是屬於 VM 的一部分。這也是爲何我很是贊同 vue2 中廢除 filter 的一部分緣由:Vue 2.0 - Bring back filters please
MVVM 利用了 Silverlight 中的數據綁定能力以及行爲和事件觸發器來管理 view 和 view-model 之間的聯繫。這些能力將業務代碼須要出如今視圖代碼中必要性變得很低。
有不少用來鏈接 view-model 和 view 的方法,包括直接的關係以及基於容器的方式。然而,全部的方式共有的一個目標就是,給 view 的 DataContext 屬性分配一個 view-model。
Views 能夠與 view models 在獨立代碼文件裏創建鏈接,也能夠直接在 view 中。
譯者:這裏提到了 silverlight 裏的 DataContext,換到 web 領域的 MVVM 框架裏咱們能夠理解成做用域,即框架經過給某個視圖的做用域分配一個 view-model 的方式,來完成 view 和 view-model 的銜接。
一個 view 的代碼能夠是在獨立代碼文件中,於此同時 view-model 須要分配成它的 DataContext 屬性。這能夠是經過一個簡單的 view 初始化一個新的 view-model 而後分配給它的 DataContext 屬性來完成,也能夠經過 view 使用控制反轉(IOC)容器注入一個 view-model 來實現。
可是,在獨立代碼中 鏈接一個 view-model 到 view 中的作法是不推薦的,它可能給同時使用 VS 及 MSB(Microsoft Expression Blend®) 設計軟件的設計師形成問題。
若是一個 view-model 沒有任何的構造器參數,它能夠被當作 view 的 DataContext 來實例化。一個一般的實現方法是使用一個 view-model 定位器。這個資源能夠公開應用的 view models 做爲屬性,從而使得獨立的 view 能夠綁定上去。這種方式意味着應用只有一個類用來鏈接 view models 到 views。此外,它仍然讓開發者能夠自由選擇手動執行 view-model 定位器內的鏈接,或者使用一個依賴注入容器。
譯者:框架實現指導
MVVM 使得完美的 開發-設計 工做流變爲可能,它具有如下優勢:
開發期間,工程師和設計師能夠更獨立並行的在各自的組件上工做。設計師能夠專一於 view,若是他們使用 Expression Blend,他們能夠輕鬆的生成示例數據,而這個時候開發者只須要專一於 view model 和 model 組件。
開發者能夠爲 view-model 和 model 編寫單元測試,而不用去管 view。view-model 的單元測試能夠徹底模擬在 view 上用的那些功能。
因爲 view 是徹底由 XAML 實現的,這也讓應用的 UI 從新設計變得簡單,並且還不用去觸碰到邏輯代碼。一個新的版本的 view 應該依然能夠與以前已存在的 view-model 一塊兒運行。
若是已經存在一個封裝好了已有業務邏輯的 model ,改這個 model 可能會比較困難或有風險。在這個場景中,view-model 應該做爲一個 model 類的適配器,從而避免 model 的代碼須要作大的變更。(譯者注:也就是說,view-model 應該對 model 中的數據作一些基礎轉換,從而去適配新的 view 或交互邏輯)
更多關於 MVVM 的信息,參考如下文檔: Implementing the MVVM Pattern.aspx) Advanced MVVM Scenarios.aspx)
MVVM 做爲2005年微軟提出來的 UI 架構,我認爲在通過這麼多年的檢驗以後,仍是很是值得信賴的。雖然在這一兩年隨着 React 的興起,以及在前端領域「起死回生」的函數式編程,MVVM 被各類錯誤或‘惡意’的解讀致使其花式被黑。可是在我看來,MVVM 做爲一個完整的 GUI 架構,跟 Flux 流派的數據層架構自己理念上是並不衝突的。咱們依然能夠 M/VM 層實踐 Flux。
我認爲就前端領域而言,MVVM 最大的意義在於,若是咱們能很乾淨的分離出應用的 view 和 M/VM,咱們就可使得整個應用的業務模型能獨立於框架運行。相比於如今換一個框架就重寫一次應用的作法(老實說我受夠了並且以爲沒什麼價值),再結合當前前端‘欣欣向榮’的狀態,若是能作到只須要花費很小的代價,咱們就能快速的享受到新技術帶來的紅利,那基本上就很是美好了。