MVVM(模型-視圖-視圖模型,Model-View-ViewModal)是一種架構模式,並不是一種框架,它是一種思想,一種組織與管理代碼的藝術。它利用數據綁定,屬性依賴,路由事件,命令等特性實現高效靈活的架構html
一個事件發生的過程:前端
一、用戶在視圖 V 上與應用發生交互vue
二、VM 觸發相應的事件,VM從模型 M 中請求到用戶須要的數據,並立馬反饋回視圖vue-router
三、視圖 V 更新數據,展示給用戶vuex
Mvvm的核心是數據驅動,實際開發中,只要預先寫好view和model的關係映射(viewmodel),而後以viewmodl爲核心,從view出發,頁面須要什麼數據,就去model中設置數據源。當發生了用戶事件時,view處理本身的用戶接口事件,並把相關事件映射到視圖模型。viewmodl通知更新model,而後刷新view。 從而實現數據雙向綁定更新。編程
模型持有着應用的多個領域下的相關數據。一個領域相關的數據,說白了,是用戶帳號(名字,頭像,電子郵件)的抽象,或者音樂唱片(唱片名,年代,專輯)的抽象。模型是一個領域下的數據及其相關邏輯的抽象。當視圖模型請求數據時,模型將數據包裝成模型實例,api
model自己是獨立的,自控的,不依賴於view,可以同步支持多view的顯示。架構
視圖是與用戶交互的一層。它是展示一個視圖模型狀態的一個可交互。視圖包含數據綁定、用戶接口事件,還須要可以理解視圖模型的行爲,儘管這些行爲可以被映射到屬性,處理這來自視圖模型的事件。mvc
視圖模型是一個專門進行數據轉換的控制器。它把對象信息轉換到視圖信息,將命令從視圖攜帶到對象。app
例子:有一個對象的日期屬性是unix格式的(e.g 1333832407),不是用戶視圖的所須要的日期格式(e.g 04/07/2012 @ 5:00pm),轉換爲視圖須要的格式。咱們的對象只簡單保存原始的unix數據格式日期,視圖模型做爲一箇中間人角色會格式化原始的unix數據格式,轉換爲視圖須要的日期格式。
在這個場景下,視圖模型至關於一個對象,它處理多個視圖顯示邏輯,也對外提供更新視圖狀態的方法,並經過視圖方法和觸發事件更新對象。
Vue描述視圖模型做爲數據的表現和操做能夠在UI上訪問和執行。視圖模型並非一個UI對象,也不是數據持久化對象,而是一個可以爲用戶提供儲存狀態及操做的層次對象。Vue的視圖模型實現了JavaScript對象與HTML語言無關性。經過這個實現使開發保持了簡單。
一切源於h5的流行,與原生app進行快速迭代。既然要用H5 來構建 App, 那View 層所作的事,就不只僅是簡單的數據展現了,它不只要管理複雜的數據狀態,還要處理移動設備上各類操做行爲等等。所以,前端須要工程化,傳統的MVC模式:
可是,當應用上升到一個級別時,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的實踐。