Vue2響應(yīng)式系統(tǒng)之嵌套
1、場(chǎng)景
在 開(kāi)發(fā)中肯定存在組件嵌套組件的情況,類(lèi)似于下邊的樣子。Vue
<!-- parent-component -->
<div>
<my-component :text="inner"></my-component>
{{ text }}
<div>
<!-- my-component-->
<div>{{ text }}</div>
回到我們之前的響應(yīng)式系統(tǒng),模擬一下上邊的情況:
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello, world",
inner: "內(nèi)部",
};
observe(data);
const updateMyComponent = () => {
console.log("子組件收到:", data.inner);
};
const updateParentComponent = () => {
new Watcher(updateMyComponent);
console.log("父組件收到:", data.text);
};
new Watcher(updateParentComponent);
data.text = "hello, liang";
可以先 分鐘考慮一下上邊輸出什么?1
首先回憶一下 會(huì)做什么操作。new Watcher
第一步是保存當(dāng)前函數(shù),然后執(zhí)行當(dāng)前函數(shù)前將全局的 賦值為當(dāng)前 對(duì)象。Dep.targetWatcher

接下來(lái)執(zhí)行 函數(shù)的時(shí)候,如果讀取了相應(yīng)的屬性就會(huì)觸發(fā) ,從而將當(dāng)前 收集到該屬性的 中。gettergetWatcherDep

2、執(zhí)行過(guò)程
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello, world",
inner: "內(nèi)部",
};
observe(data);
const updateMyComponent = () => {
console.log("子組件收到:", data.inner);
};
const updateParentComponent = () => {
new Watcher(updateMyComponent);
console.log("父組件收到:", data.text);
};
new Watcher(updateParentComponent);
data.text = "hello, liang";
我們?cè)僖徊揭徊嚼砬逡幌拢?/strong>
new Watcher(updateParentComponent);
將 賦值為保存了 函數(shù)的 。Dep.targetupdateParentComponentWatcher
接下來(lái)執(zhí)行 函數(shù)。updateParentComponent
new Watcher(updateMyComponent);
將 賦值為保存了 函數(shù)的 。Dep.targetupdateMyComponentWatcher
接下來(lái)執(zhí)行 函數(shù)。updateMyComponent
const updateMyComponent = () => {
console.log("子組件收到:", data.inner);
};
// 讀取了 inner 變量。
// data.inner 的 Dep 收集當(dāng)前 Watcher(保存了 `updateMyComponent` 函數(shù))const updateParentComponent = () => {
new Watcher(updateMyComponent);
console.log("父組件收到:", data.text);
};
// 讀取了 text 變量。
// data.text 的 Dep 收集當(dāng)前 Watcher (保存了 `updateMyComponent` 函數(shù))
data.text = "hello, liang";
觸發(fā) 的 函數(shù),執(zhí)行它依賴(lài)的 ,而此時(shí)是 函數(shù)。textsetWatcherupdateMyComponent
所以上邊代碼最終輸出的結(jié)果是:
子組件收到: 內(nèi)部 // new Watcher(updateMyComponent); 時(shí)候輸出
父組件收到: hello, world // new Watcher(updateParentComponent); 時(shí)候輸出
子組件收到: 內(nèi)部 // data.text = "hello, liang"; 輸出
然而子組件并不依賴(lài) ,依賴(lài) 的父組件反而沒(méi)有執(zhí)行。data.textdata.text
3、修復(fù)
上邊的問(wèn)題出在我們保存當(dāng)前正在執(zhí)行 時(shí)候使用的是單個(gè)變量 。WatcherDep.target = null; // 靜態(tài)變量,全局唯一
回憶一下學(xué)習(xí) 語(yǔ)言或者匯編語(yǔ)言的時(shí)候?qū)瘮?shù)參數(shù)的處理:C
function b(p) {
console.log(p);
}
function a(p) {
b("child");
console.log(p);
}
a("parent");
當(dāng)函數(shù)發(fā)生嵌套調(diào)用的時(shí)候,執(zhí)行 函數(shù)的時(shí)候我們會(huì)先將參數(shù)壓入棧中,然后執(zhí)行 函數(shù),同樣將參數(shù)壓入棧中, 函數(shù)執(zhí)行完畢就將參數(shù)出棧。此時(shí)回到 函數(shù)就能正確取到 參數(shù)的值了。abbap
對(duì)應(yīng)于 的收集,我們同樣可以使用一個(gè)棧來(lái)保存,執(zhí)行函數(shù)前將 壓入棧,執(zhí)行函數(shù)完畢后將 彈出棧即可。其中, 始終指向棧頂 ,代表當(dāng)前正在執(zhí)行的函數(shù)。WatcherWatcherWatcherDep.targetWatcher
回到 代碼中,我們提供一個(gè)壓棧和出棧的方法。Dep
import { remove } from "./util";
let uid = 0;
export default class Dep {
... 省略
}
Dep.target = null; // 靜態(tài)變量,全局唯一
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
const targetStack = [];
export function pushTarget(target) {
targetStack.push(target);
Dep.target = target;
}
export function popTarget() {
targetStack.pop();
Dep.target = targetStack[targetStack.length - 1]; // 賦值為棧頂元素
}
然后 中,執(zhí)行函數(shù)之前進(jìn)行入棧,執(zhí)行后進(jìn)行出棧。Watcher
import { pushTarget, popTarget } from "./dep";
export default class Watcher {
constructor(Fn) {
this.getter = Fn;
this.depIds = new Set(); // 擁有 has 函數(shù)可以判斷是否存在某個(gè) id
this.deps = [];
this.newDeps = []; // 記錄新一次的依賴(lài)
this.newDepIds = new Set();
this.get();
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
get() {
/************修改的地方*******************************/
pushTarget(this); // 保存包裝了當(dāng)前正在執(zhí)行的函數(shù)的 Watcher
/*******************************************/
let value;
try {
value = this.getter.call();
} catch (e) {
throw e;
} finally {
/************修改的地方*******************************/
popTarget();
/*******************************************/
this.cleanupDeps();
}
return value;
}
...
}
4、測(cè)試
回到開(kāi)頭的場(chǎng)景,再來(lái)執(zhí)行一下:
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello, world",
inner: "內(nèi)部",
};
observe(data);
const updateMyComponent = () => {
console.log("子組件收到:", data.inner);
};
const updateParentComponent = () => {
new Watcher(updateMyComponent);
console.log("父組件收到:", data.text);
};
new Watcher(updateParentComponent);
data.text = "hello, liang";
執(zhí)行 的時(shí)候?qū)?nbsp;入棧。new Watcher(updateParentComponent);Watcher

進(jìn)入 函數(shù),執(zhí)行 的時(shí)候?qū)?nbsp;入棧。updateParentComponentnew Watcher(updateMyComponent);Watcher

執(zhí)行 函數(shù), 收集當(dāng)前 ,執(zhí)行完畢后 出棧。updateMyComponentdata.innerDep.targetWatcher

繼續(xù)執(zhí)行 函數(shù), 收集當(dāng)前 。updateParentComponentdata.textDep.target
此時(shí)依賴(lài)就變得正常了, 會(huì)觸發(fā) 函數(shù),從而輸出如下:data.textupdateParentComponent
子組件收到: 內(nèi)部
父組件收到: hello, world
子組件收到: 內(nèi)部
父組件收到: hello, liang
5、總結(jié)
今天這個(gè)相對(duì)好理解一些,通過(guò)棧解決了嵌套調(diào)用的情況。
到此這篇關(guān)于Vue2響應(yīng)式系統(tǒng)之嵌套的文章就介紹到這了,更多相關(guān)Vue2 嵌套內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Vue.js中ref ($refs)用法舉例總結(jié)
本篇文章主要介紹了淺談Vue.js中ref ($refs)用法舉例總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
vue實(shí)現(xiàn)簡(jiǎn)單的購(gòu)物車(chē)小案例
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)簡(jiǎn)單的購(gòu)物車(chē)小案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
vue項(xiàng)目keepAlive配合vuex動(dòng)態(tài)設(shè)置路由緩存方式
vue項(xiàng)目keepAlive配合vuex動(dòng)態(tài)設(shè)置路由緩存方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
如何使用 Vue Router 的 meta 屬性實(shí)現(xiàn)多種功能
在Vue.js中,Vue Router 提供了強(qiáng)大的路由管理功能,通過(guò)meta屬性,我們可以在路由定義中添加自定義元數(shù)據(jù),以實(shí)現(xiàn)訪問(wèn)控制、頁(yè)面標(biāo)題設(shè)置、角色權(quán)限管理、頁(yè)面過(guò)渡效果,本文將總結(jié)如何使用 meta 屬性來(lái)實(shí)現(xiàn)這些常見(jiàn)的功能,感興趣的朋友一起看看吧2024-06-06
利用Dectorator分模塊存儲(chǔ)Vuex狀態(tài)的實(shí)現(xiàn)
這篇文章主要介紹了利用Dectorator分模塊存儲(chǔ)Vuex狀態(tài)的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02

