MVVM 設計模式,是由 MVC、MVP 等設計模式進化而來,M - 資料模型(Model),VM - 視圖模型(ViewModel),V - 視圖層(View)。MVVM 的核心是 ViewModel 層,它就像是一個中轉站(value converter),負責轉換 Model 中的資料對象來讓資料變得更容易管理和使用,該層向上與視圖層進行雙向資料綁定,向下與 Model 層通過接口請求進行資料互動,起呈上啟下作用。如下圖所示:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLi0zaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLwkzX39GZhh2csATMflHLwEzX4xSZz91ZsADMx8FdsYkRGZkRG9lcvx2bjxSa2EWNhJTW1AlUxEFeVRUUfRHelRHL2EzXlpXazxyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3PnVGcq5SZkdjNxQ2MjJzYlNjZ2IWNwYjM4ADZxQ2NiNTNjVzY58CX4AzLclDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL2M3Lc9CX6MHc0RHaiojIsJye.jpeg)
使用 MVVM 設計模式的前端架構很多,其中漸進式架構 Vue 是典型的代表,深得廣大前端開發者的青睐。
從上圖中可以看出MVVM主要分為這麼幾個部分:
模闆編譯(Compile)
資料劫持(Observer)
訂閱-釋出(Dep)
觀察者(Watcher)
我們來看一個 vue 的執行個體:
我們對原理進行分析一下:
首先 <code>new Vue()</code> 執行初始化,通過 <code>Observer</code> 對 <code>data</code> 上的屬性執行響應式處理。也就是 <code>Object.defineProperty</code> 對資料屬性進行劫持。
通過<code>Compile</code> 進行模闆編譯,對模闆裡動态綁定的資料,使用 <code>data</code> 資料進行初始化。
在模闆初始化時,在觸發<code>Object.defineProperty</code> 内的 <code>getter</code> 時,建立更新函數和 <code>Watcher</code> 類。
同一屬性在模闆中可能出現多次,就會建立多個<code>Watcher</code> ,就需要<code>Dep</code> 來統一管理。
當資料發生變化時,找到屬性對應的 <code>dep</code> ,通知所有 <code>Watcher</code> 執行更新函數。
上面代碼建立了一個 <code>Vue</code> 類和 <code>proxyData</code> 方法,<code>Vue</code> 類接收 <code>options</code> 參數,内部調用 <code>observe</code> 方法對傳入參數 <code>options.data</code> 資料,遞歸進行響應式處理。
使用 <code>proxyData</code> 方法把資料代理到執行個體上,讓我們擷取和修改資料的時候可以直接通過 <code>this</code> 或 <code>this.$data</code>, 如: <code>this.number</code> 或 <code>this.$data.number</code> 。
最後使用 <code>Compiler</code> 對模闆進行編譯,初始化動态綁定的資料。
編譯過程中,以根元素開始,也就是執行個體化 <code>Vue</code> 時傳入的 <code>options.el</code> 進行遞歸編譯節點,使用 <code>isElementNode</code> 方法判斷是文本節點還是元素節點。
如果是文本節點,正則比對(雙大括号)<code>{{ xxx }}</code>;使用 <code>v-text</code> 指令方式初始化讀取資料。
若為元素節點,周遊屬性,找到 <code>v-text</code> 或 <code>v-html</code>,初始化動态綁定的資料。
在初始化資料時,建立 <code>Watcher</code> 和更新函數。
上面代碼,建立了 <code>observe</code> 和 <code>defineReactive</code> 方法,還有 <code>Observer</code> 類 。
<code>observe</code> 方法用于類型判斷。
<code>Observer</code> 類收一個參數,若參數是 object 類型,調用 <code>defineReactive</code> 方法對其屬性進行劫持。
在 <code>defineReactive</code> 方法中,通過 <code>Object.defineProperty</code> 對屬性進行劫持。并對每個 <code>key</code> 建立 <code>Dep</code> 的執行個體,還記得模闆編譯時,對動态綁定的值,進行初始化的時候會建立 <code>Watcher</code> 嗎?<code>Watcher</code> 内儲存有對應的更新函數;<code>defineReactive</code> 中,資料被讀取的時候,就會觸發 <code>getter</code> , <code>getter</code> 中就會把 <code>Watcher</code> push 到對應的 <code>Dep</code> 中,這個過程就叫做依賴收集。當值發生改變的時候,觸發 <code>setter</code> 調用這個<code>key</code> 所對應的 <code>Dep</code> 内的 <code>notify </code> 方法,通知更新。
每個 <code>key</code> 都會建立一個 <code>Dep</code> ,每個 <code>Dep</code> 内都會有一個 <code>deps</code> 數組,用來同一管理這個 <code>key</code> 所對應的 <code>Watcher</code> 執行個體。
<code>addDep</code> 方法用于添加訂閱。
<code>notify</code> 方法用于通知更新。
<code>Watcher</code> 類接收三個參數,<code>Vue</code> 的執行個體、 綁定的變量名 <code>key</code> 和 <code>updateFn</code> 更新方法。<code>Watcher</code> 在模闆編譯時被建立。我們用 <code>Dep.target</code> 靜态屬性來儲存目前的執行個體。主動觸發一次響應式的 <code>getter</code> , 使其執行個體被添加到 <code>Dep</code> 中,完成依賴收集,完成後,将靜态屬性 <code>Dep.target</code> 置空。
内部建立一個 <code>update</code> 更新方法。在 <code>Dep</code> 的 <code>notify</code> 方法通知更新時被調用。