為何要進(jìn)行依賴收集
先看下面這段代碼
new Vue({
template:
`<div>
<span>text1:</span> {{text1}}
<span>text2:</span> {{text2}}
<div>`,
data: {
text1: 'text1',
text2: 'text2',
text3: 'text3'
}
});
按照之前的文章響應(yīng)式原理中的方法綁定則會出現(xiàn)一個問題---text3在實際模板中并沒有被用到歹叮,然而當(dāng)text3的數(shù)據(jù)被修改的時候(this.text3='test')的時候村怪,同樣會觸發(fā)text3的setter導(dǎo)致重新執(zhí)行渲染查近,這顯然不正確酷誓。
先說說Dep
當(dāng)對data上的對象進(jìn)行修改值的時候會觸發(fā)它的setter隔箍,那么取值的時候自然就會觸發(fā)setter事件阳懂,所以我們只要在最開始進(jìn)行一次render寻定,那么所有被渲染所依賴的data中的數(shù)據(jù)就會被getter收集到Dep的subs中去儒洛。在對data中的數(shù)據(jù)進(jìn)行修改的時候setter只會觸發(fā)Dep的subs的函數(shù)
定義一個依賴收集類Dep。
class Dep () {
constructor () {
this.subs = [];
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
/*Github:https://github.com/answershuto*/
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
Watcher
訂閱者狼速,當(dāng)依賴收集的時候會addSub到sub中琅锻,在修改data中數(shù)據(jù)的時候會觸發(fā)dep對象的notify,通知所有Watcher對象去修改對應(yīng)視圖
class Watcher () {
constructor (vm, expOrFn, cb, options) {
this.cb = cb;
this.vm = vm;
/*在這里將觀察者本身賦值給全局的target向胡,只有被target標(biāo)記過的才會進(jìn)行依賴收集*/
Dep.target = this;
/*Github:https://github.com/answershuto*/
/*觸發(fā)渲染操作進(jìn)行依賴收集*/
this.cb.call(this.vm);
}
update () {
this.cb.call(this.vm);
}
}
開始依賴收集
class Vue {
constructor(options) {
this._data = options.data;
observer(this._data, options.render);
let watcher = new Watcher(this, );
}
}
function defineReactive (obj, key, val, cb) {
/*在閉包內(nèi)存儲一個Dep對象*/
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=>{
if (Dep.target) {
/*Watcher對象存在全局的Dep.target中*/
dep.addSub(Dep.target);
}
},
set:newVal=> {
/*只有之前addSub中的函數(shù)才會觸發(fā)*/
dep.notify();
}
})
}
Dep.target = null;
將觀察者Watcher實例賦值給全局的Dep.target恼蓬,然后觸發(fā)render操作只有被Dep.target標(biāo)記過的才會進(jìn)行依賴收集。有Dep.target的對象會將Watcher的實例push到subs中僵芹,在對象被修改觸發(fā)setter操作的時候dep會調(diào)用subs中的Watcher實例的update方法進(jìn)行渲染处硬。