Vue2剝絲抽繭-響應(yīng)式系統(tǒng)完善
這篇文章主要修之前代碼存在的一個(gè)問題,廢話不多說,上代碼!
場(chǎng)景
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello, world",
};
observe(data);
let show = true;
const updateComponent = () => {
if (show) {
console.log(data.text);
show = false;
}
};
new Watcher(updateComponent);
new Watcher(() => console.log("依賴", data.text));
data.text = "123";
先可以 1 分鐘思考一下會(huì)輸出什么。
- new Watcher(updateParentComponent);
執(zhí)行 updateParentComponent 函數(shù),輸出 hello, world,并且 text 的 Dep 收集該 Watcher 。
- new Watcher(() => console.log("依賴", data.text));
執(zhí)行匿名函數(shù),輸出 依賴 hello, world ,并且 text 的 Dep 收集該 Watcher 。
- data.text = "123"; 。
觸發(fā) text 的 set,依次執(zhí)行 Dep 中的 Watcher 。
先執(zhí)行 updateParentComponent 。
const updateComponent = () => {
if (show) {
console.log(data.text);
show = false;
}
};
由于之前已經(jīng)執(zhí)行過一次了,此時(shí) show 就是 false 了,什么都不會(huì)輸出。
再執(zhí)行 () => console.log("依賴", data.text) ,輸出 依賴 hello, world。
是的,上邊是我們所期望的樣子,但事實(shí)上輸出結(jié)果如下:
出錯(cuò)代碼 dep.js:37:26 如下:
調(diào)用 update 的時(shí)候是,遍歷過程中 subs[i] 變成了 undefined ,導(dǎo)致了報(bào)錯(cuò)。
需要回憶下 Vue2剝絲抽繭-響應(yīng)式系統(tǒng)之分支切換 這篇文章里我們做了什么。
如果 Watcher 中的函數(shù)不再依賴當(dāng)前屬性,我們就把當(dāng)前 Watcher 從該屬性的 Dep 中移除。
而移除其實(shí)就是調(diào)用了數(shù)組的 splice 方法,直接將 Dep 中的 subs 數(shù)組元素進(jìn)行刪除。
removeSub(sub) {
remove(this.subs, sub);
}
export function remove(arr, item) {
if (arr.length) {
const index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1);
}
}
}
而此時(shí)我們正在遍歷 subs 數(shù)組:
notify() {
for (let i = 0, l = this.subs.length; i < l; i++) {
this.subs[i].update();
}
}
對(duì)應(yīng)上邊的例子,原本 subs 數(shù)組兩個(gè) Watcher,第一個(gè) Watcher 執(zhí)行的時(shí)候沒有訪問 data.text 屬性,就要把這一個(gè) Watcher 刪除了,第二個(gè)就移動(dòng)到第一個(gè)的位置了,此時(shí) for 循環(huán)中訪問第二個(gè)位置的 Watcher 因?yàn)楸灰频角斑呑匀痪蛨?bào)錯(cuò)了。
修改起來也很容易,我們只需要在循環(huán)前,將原有的 subs 數(shù)組保存給一個(gè)新的數(shù)組即可。
notify() {
// stabilize the subscriber list first
const subs = this.subs.slice();
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
}
總結(jié)
這篇文章比較簡(jiǎn)單,主要就是循環(huán)通知 Watcher 之前把列表另存起來,防止遍歷過程中被修改。