天天看點

Vue(v2.6.14)源碼解毒(預):手寫一個簡易版Vue

MVVM 設計模式,是由 MVC、MVP 等設計模式進化而來,M - 資料模型(Model),VM - 視圖模型(ViewModel),V - 視圖層(View)。MVVM 的核心是 ViewModel 層,它就像是一個中轉站(value converter),負責轉換 Model 中的資料對象來讓資料變得更容易管理和使用,該層向上與視圖層進行雙向資料綁定,向下與 Model 層通過接口請求進行資料互動,起呈上啟下作用。如下圖所示:

Vue(v2.6.14)源碼解毒(預):手寫一個簡易版Vue

使用 MVVM 設計模式的前端架構很多,其中漸進式架構 Vue 是典型的代表,深得廣大前端開發者的青睐。

Vue(v2.6.14)源碼解毒(預):手寫一個簡易版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>​ 方法通知更新時被調用。