Vue雙向資料繫結的核心和基礎api是Object.defineProperty,其內部真正參與資料雙向繫結流程的主要有Obderver、Dep和Watcher,基於defineProperty和釋出者訂閱者模式,最終實現資料的雙向繫結。那麼Obderver、Dep和Watcher是如何具體配合工作的呢?下面就來理一理。
看此文章之前你需要對vue的雙向資料繫結有一定的理解。若不瞭解可移步:vue.js原始碼解讀系列 – 雙向繫結具體如何初始化和工作
看到這裡就當你對雙向資料繫結已經有一定的理解:
提示:要看懂此篇文章你需要對vue的mvvm有一定的瞭解,並需要和專注的去理解,或者對照原始碼跟著走,不然就很難真的看懂。
在這裡把雙向資料繫結分為兩個流程:
1、收集依賴流程:
observe ->
walk ->
defineReactive ->
get ->
dep.depend() ->
watcher.addDep(new Dep()) ->
watcher.newDeps.push(dep) ->
dep.addSub(new Watcher()) ->
dep.subs.push(watcher)複製程式碼
依賴收集會經過以上流程,最終watcher.newDeps陣列中存放dep列表,dep.subs陣列中存放watcher列表。
為什麼要進行依賴收集?
new Vue({
data(){
return {
name:`zane`,
sex:`男`
}
}
})複製程式碼
有上面這個data,實際上頁面只使用到了name,並沒有使用age,根據Object.defineProperty的轉換,如果我們設定了this.sex=`女`,那麼Vue也會去執行一遍虛擬DOM的比較,這樣就無形的浪費了一些效能,因此才需要做依賴收集,介面用到了就收集,沒有用到就不收集。
我們跟著流程走來理一遍原始碼:
直接進入Object.defineProperty的get方法:
考驗你閉包能力的時候到了,這個dep物件就是一個閉包。記下來我們看看dep.depend()方法的實現。
先暫停一下,上面兩處都用到了 Dep.target ,我也說了它就是一個Watcher例項化物件,你是不是很想搞懂它到底在哪裡賦值的呢,不急請跟著我下面的程式碼看看。
搞懂了Dep.target等於一個Watche物件,現在繼續回到之前的思路看watcher.addDep做了什麼。
就這樣依賴收集的流程就走完了,是否感覺很繞。
總結:依賴收集最終在 watcher.newDeps 中push了閉包中傳過來的dep物件,在dep.subs中push了初始化Vue是簡歷的Watcher物件,這個物件的,this.getter = expOrFn,傳過來的expOrFn是後期資料更新頁面渲染的核心步驟,需要沉下心來好好去理理。
2、檢視更新流程:
set ->
dep.notify() ->
subs[i].update() ->
watcher.run() || queueWatcher(this) ->
watcher.get() || watcher.cb ->
watcher.getter() ->
vm._update() ->
vm.__patch__()複製程式碼
檢視更新會經過以上流程,最終呼叫Vue的虛擬Dom diff過程實時更新介面檢視
走到此處後面我就不去跟蹤了,後面會呼叫vm.__patch__ 方法,進而執行虛擬DOM的diff過程實時的更新介面。
總結:
要很好的理解vue的資料雙向繫結就要比較耐心,沉下心來慢慢理解,同時也需要對vue的原始碼有個大致的理解,不然你只會看的越來越煩躁越來越沒有信心。
vue很好的利用了Object.defineProperty方法的 get和set方法,訂閱者釋出者的設計思路,巧妙的組織程式碼,值得我們很深入的去學習和理解,從而促使我們更好的去使用它。謝謝尤大的無私奉獻,讓我們提高了生產力,把更多的精力花到業務邏輯中去。