什麼是MVVM? MVVM(模型-視圖-視圖模型,Model-View-ViewModal)是一種架構模式,並非一種框架,它是一種思想,一種組織與管理代碼的藝術。它利用數據綁定,屬性依賴,路由事件,命令等特性實現高效靈活的架構 一個事件發生的過程: 1、用戶在視圖 V 上與應用發生交互 2、VM 觸 ...
什麼是MVVM?
MVVM(模型-視圖-視圖模型,Model-View-ViewModal)是一種架構模式,並非一種框架,它是一種思想,一種組織與管理代碼的藝術。它利用數據綁定,屬性依賴,路由事件,命令等特性實現高效靈活的架構
一個事件發生的過程:
1、用戶在視圖 V 上與應用發生交互
2、VM 觸發相應的事件,VM從模型 M 中請求到用戶需要的數據,並立馬反饋回視圖
3、視圖 V 更新數據,展現給用戶
Mvvm的核心是數據驅動,實際開發中,只要預先寫好view和model的關係映射(viewmodel),然後以viewmodl為核心,從view出發,頁面需要什麼數據,就去model中設置數據源。當發生了用戶事件時,view處理自己的用戶介面事件,並把相關事件映射到視圖模型。viewmodl通知更新model,然後刷新view。 從而實現數據雙向綁定更新。
概念簡介
一)模型
模型持有著應用的多個領域下的相關數據。一個領域相關的數據,說白了,是用戶賬號(名字,頭像,電子郵件)的抽象,或者音樂唱片(唱片名,年代,專輯)的抽象。模型是一個領域下的數據及其相關邏輯的抽象。當視圖模型請求數據時,模型將數據包裝成模型實例,
model本身是獨立的,自控的,不依賴於view,能夠同步支持多view的顯示。
二)視圖
視圖是與用戶交互的一層。它是展現一個視圖模型狀態的一個可交互。視圖包含數據綁定、用戶介面事件,還需要能夠理解視圖模型的行為,儘管這些行為能夠被映射到屬性,處理這來自視圖模型的事件。
三)視圖模型(核心)
視圖模型是一個專門進行數據轉換的控制器。它把對象信息轉換到視圖信息,將命令從視圖攜帶到對象。
例子:有一個對象的日期屬性是unix格式的(e.g 1333832407),不是用戶視圖的所需要的日期格式(e.g 04/07/2012 @ 5:00pm),轉換為視圖需要的格式。我們的對象只簡單保存原始的unix數據格式日期,視圖模型作為一個中間人角色會格式化原始的unix數據格式,轉換為視圖需要的日期格式。
在這個場景下,視圖模型相當於一個對象,它處理多個視圖顯示邏輯,也對外提供更新視圖狀態的方法,並通過視圖方法和觸發事件更新對象。
Vue描述視圖模型作為數據的表現和操作可以在UI上訪問和執行。視圖模型並不是一個UI對象,也不是數據持久化對象,而是一個能夠為用戶提供儲存狀態及操作的層次對象。Vue的視圖模型實現了JavaScript對象與HTML語言無關性。通過這個實現使開發保持了簡單。
為什麼會出現 MVVM 呢?
一切源於h5的流行,與原生app進行快速迭代。既然要用H5 來構建 App, 那View 層所做的事,就不僅僅是簡單的數據展示了,它不僅要管理複雜的數據狀態,還要處理移動設備上各種操作行為等等。因此,前端需要工程化,傳統的MVC模式:
- View 展示數據
- Model 管理數據
- Controller 響應用戶操作,並將 Model 更新到 View 上
但是,當應用上升到一個級別時,mvc模式的弊端有3個明顯的問題:
1、代碼中大量調用相同的 DOM API,代碼難以維護。
2、大量的DOM 操作使頁面載入速度變慢,影響用戶體驗。
3、Model 的頻繁變化,需要開發者主動更新到View ;當用戶的操作導致 Model 發生變化,開發者同樣需要將變化的數據同步到Model 中。 當UI 狀態一旦多起來時,這些工作不僅繁瑣,而且很難維護複雜多變的數據狀態。
關於對MVC比較詳細的理解,這裡請參考我寫的上一篇文章:簡單談談Mvc
為瞭解決上述問題,出現了前端界的MVVM。MVVM 可以很好的降低我們維護狀態,視圖的複雜程度(大大減少代碼中的視圖更新邏輯)。
下麵還是以todoList為demo和上篇文章的例子對比,實現同樣的功能,用到的js代碼不到30行
1 <template> 2 <div id="app"> 3 <ul v-for="(item, index) in todoList"> 4 <li @click="remove(index)">{{item.text}}</li> 5 </ul 6 <input type="text" v-model="text"> 7 <button @click="add">確認</button> 8 </div> 9 </template> 10 11 <script> 12 import store from './data_store.js' 13 14 const TODO_LIST = '__todoList__' 15 export default { 16 data() { 17 return { 18 text: '', 19 todoList: store.get(TODO_LIST, []) 20 } 21 }, 22 23 methods: { 24 add() { 25 let val = this.todoList.push({ 26 id: Number(new Date()), 27 text: this.text && this.text.trim() 28 }) 29 this.text = '' 30 }, 31 remove(index) { 32 this.todoList.splice(index, 1) 33 } 34 }, 35 watch: { 36 todoList() { 37 store.set(TODO_LIST, this.todoList) 38 } 39 } 40 } 41 </script>
1 /* 2 * 只封了 get 與 set 3 */ 4 let store = { 5 storage: window.localStorage 6 } 7 8 const api = { 9 /* 10 * @param key 為localStorage 的key值 11 * @param defaults 當本地存儲的數據為空時的預設值 12 */ 13 get(key, defaults) { 14 let val = deserialize(this.storage.getItem(key)) 15 return val !== undefined ? val : defaults 16 }, 17 18 set(key, val) { 19 if (typeof val === "undefined") { 20 return this.remove(key) 21 } 22 this.storage.setItem(key, serialize(val)) 23 }, 24 25 remove(key) { 26 this.storage.removeItem(key) 27 } 28 } 29 30 function serialize(val) { 31 return JSON.stringify(val) 32 } 33 34 function deserialize(val) { 35 if (typeof val !== "string") { 36 return 37 } 38 return JSON.parse(val) 39 } 40 41 Object.assign(store, api) 42 43 export default store
解決什麼?
對於有一定數量功能的網頁,合理,高效地組織代碼,是提高開發效率的關鍵所在。在事件管理上面,MV*註重模型的數據改變而觸發各種事件,將數據和事件聯繫起來,數據變動,界面變化。面向數據編程,把所有精力放在數據處理,不關心對網頁元素的處理。MVVM更加便於UI和驅動UI的構造塊,這兩部分的並行開發,抽象視圖使得背後所需要的業務邏輯(或者粘合劑)的代碼數量得以減少,對於持續集成項目,你不光要考慮到初次開發,還要考慮功能演進和可交接性。
從前端地角度,它是UI模式的解決方案,在前端,我們經常要處理數據與界面的關係。m與v的完全脫離,使得開發人員只專於註業務邏輯,抽象的數據,依靠vm與v的雙向綁定,通過改變業務邏輯,界面就自動更新了,尤其方便。故開發人員需要維護的只是抽象數據,通過數據,可以隨時構建出新的 UI 。 當 UI 的狀態一旦多起來時,mvvm這種優勢就體現出來了。
當下優秀的MVVM框架有很多,不同的業務場景採用不同框架,它們有一個始終統一的目的:解放dom操作,面向數據編程。這裡以vue為例,在同一業務邏輯下,通過vue很好地解決了m與v的耦合,其高可復用性,一個viewModal可以復用到多個view視圖上。開發人員只關註viewModal,結合其生態系統中的vue-router與vuex更好地組織代碼。純粹講MVVM的概念太多抽象了,在下一篇文章,我會通過實現一個簡單的vue來模擬mvvm的實踐。