依賴收集的始末
你好! 這是一篇記載關於Vue源碼中依賴收集過程的文章。
從vue初始化開始
當我們用new操作符構建一個新的Vue實例時會發生:
- 初始化,將 root 始終指向根實例,根節點唯一,且爲最初聲明的Vue實例,後續的組件都是 $root 的children,源碼中
this.\$root = this.$parent.$root || this
,其他的就是將屬性置爲初始值; - 混入來自Vue構造函數的默認指令以及options,如 v-model,v-text ;
- 調用beforeCreated,此時data,computed,methods等還未初始化,故無法進行異步請求來賦值;
- 依次進行mixins混入,組件,props,methods,data,computed,watch,events 初始化,混入時讀取mixins內的 options ,若原始的options 內的基本類型屬性不在 mixins 內找到,則會添加,否則保留原始的數據。如果時引用類型,methods watch computed 不替換,若是生命週期,則會將mixins內的處理邏輯添加至對應鉤子的事件隊列中。對組件進行 init 操作得到一個 Vue 實例,將其添加至當前實例的 components 上。將對應的props,methods,data,computed綁定在vm上,不同的是 computed 和 watch 會創建 Watcher 實例,且computed是lazy,即如果依賴的數據未發生變化,會使用之前的緩存,而 data 會做數據劫持生成自己的唯一Dep;如果當前組件是子組件則會讀取組件標籤上的
@或者v-on
指令,並會在當前實例上通過 $on 註冊事件,如果當前組建內有調用 $emit 進行觸發,則會執行註冊事件的回調。 - 初始化完成,調用created,此時可以通過 this 對值進行修改以及觸發事件;
- 進入compile階段;
依賴收集
- 觸發途徑
- 處理過程
所有通過 new 操作符調用 Watcher 生成新對象的行爲都會產生依賴收集,比如computed,watch,模板文本 {{obj.a}} ,v-bind,v-model 等都會產生依賴收集行爲。
當有新的Watcher實例生成時會執行get方法,而get方法內的代碼是這樣的:
get() {
const vm = this.vm
// 在讀取值時先將觀察者對象賦值給Dep.target 否則Dep.target爲空 不會觸發收集依賴
Dep.target = this
let value = this.getter.call(vm, vm)
if (this.filters) {
value = vm._applyFilters(value, this.filters)
}
// 觸發依賴後置爲空
Dep.target = null
return value
},
首先會將Dep.target設置爲當前 Watcher 實例,同時訪問表達式,即如果監聽的是 obj.a ,則會觸發 obj.a 的 get ,由於初始化時被劫持,同時生成了專屬Dep ,
function defineReactive(obj, key, val) {
const dep = new Dep()
console.log(dep)
// 遞歸監聽
let childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 收集對應的觀察者對象
if (Dep.target) {
dep.depend()
console.log(dep)
if (childOb) {
childOb.dep.depend()
}
if (isArray(val)) {
for (let e, i = 0, l = val.length; i < l; i++) {
e = val[i]
e && e.__ob__ && e.__ob__.dep.depend()
}
}
}
return val
},
set(newVal) {
if (val === newVal) {
return
}
val = newVal
// 遞歸監聽
childOb = observe(newVal)
// 觸發更新
dep.notify()
}
})
}
dep會調用depend函數,將watcher加入自己的subs,同時watcher也會將dep加入自己的deps,這樣當值發生變化時,調用dep.notify通知dep下所有subs中的watcher 執行 update 操作 ,而後就可以在wacher 的 callback 內拿到新舊數據了。