成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Vue2剝絲抽繭-響應(yīng)式系統(tǒng)之分支切換

開發(fā) 前端
今天這個(gè)主要就是對(duì)響應(yīng)式系統(tǒng)的一點(diǎn)優(yōu)化,避免不必要的重新執(zhí)行。所做的事情就是重新調(diào)用函數(shù)的時(shí)候,把已經(jīng)沒有關(guān)聯(lián)的 Watcher 去除。

場景

我們考慮一下下邊的代碼會(huì)輸出什么。

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello, world",
ok: true,
};
observe(data);

const updateComponent = () => {
console.log("收到", data.ok ? data.text : "not");
};

new Watcher(updateComponent); // updateComponent 執(zhí)行一次函數(shù),輸出 hello, world

data.ok = false; // updateComponent 執(zhí)行一次函數(shù),輸出 not

data.text = "hello, liang"; // updateComponent 會(huì)執(zhí)行嗎?

我們來一步一步理清:

observer(data)

攔截了 data 中 text 和 ok 的 get、set,并且各自初始化了一個(gè) Dep 實(shí)例,用來保存依賴它們的 Watcher 對(duì)象。

new Watcher(updateComponent);

這一步會(huì)執(zhí)行 updateComponent 函數(shù),執(zhí)行過程中用到的所有對(duì)象屬性,會(huì)將 Watcher 收集到相應(yīng)對(duì)象屬性中的Dep 中。

當(dāng)然這里的 Watcher 其實(shí)是同一個(gè),所以用了指向的箭頭。

data.ok = false;

這一步會(huì)觸發(fā) set ,從而執(zhí)行 Dep 中所有的 Watcher ,此時(shí)就會(huì)執(zhí)行一次 updateComponent 。

執(zhí)行 updateComponent 就會(huì)重新讀取 data 中的屬性,觸發(fā) get,然后繼續(xù)收集 Watcher 。

重新執(zhí)行 updateComponent 函數(shù) 的時(shí)候:

const updateComponent = () => {
console.log("收到", data.ok ? data.text : "not");
};

因?yàn)?data.ok 的值變?yōu)?false ,所以就不會(huì)觸發(fā) data.text 的 get ,text 的 Dep 就不會(huì)變化了。

而 data.ok 會(huì)繼續(xù)執(zhí)行,觸發(fā) get 收集 Watcher ,但由于我們 Dep 中使用的是數(shù)組,此時(shí)收集到的兩個(gè) Wacher 其實(shí)是同一個(gè),這里是有問題,會(huì)導(dǎo)致 updateComponent 重復(fù)執(zhí)行,一會(huì)兒我們來解決下。

data.text = "hello, liang";

執(zhí)行這句的時(shí)候,會(huì)觸發(fā) text 的 set,所以會(huì)執(zhí)行一次 updateComponent 。但從代碼來看 updateComponent 函數(shù)中由于 data.ok 為 false,data.text 對(duì)輸出沒有任何影響,這次執(zhí)行其實(shí)是沒有必要的。

之所以執(zhí)行了,是因?yàn)榈谝淮螆?zhí)行 updateComponent 讀取了 data.text 從而收集了 Watcher ,第二次執(zhí)行 updateComponent 的時(shí)候,data.text 雖然沒有讀到,但之前的 Watcher 也沒有清除掉,所以這一次改變 data.text 的時(shí)候 updateComponent 依舊會(huì)執(zhí)行。

所以我們需要的就是當(dāng)重新執(zhí)行 updateComponent 的時(shí)候,如果 Watcher 已經(jīng)不依賴于某個(gè) Dep 了,我們需要將當(dāng)前 Watcher 從該 Dep 中移除掉。

問題

總結(jié)下來我們需要做兩件事情。

  • 去重,Dep 中不要重復(fù)收集 Watcher 。
  • 重置,如果該屬性對(duì) Dep 中的 Wacher 已經(jīng)沒有影響了(換句話就是,Watcher 中的 updateComponent 已經(jīng)不會(huì)讀取到該屬性了 ),就將該 Watcher 從該屬性的 Dep 中刪除。

去重

去重的話有兩種方案:

  • Dep 中的 subs 數(shù)組換為 Set。
  • 每個(gè) Dep 對(duì)象引入 id ,Watcher 對(duì)象中記錄所有的 Dep 的 id,下次重新收集依賴的時(shí)候,如果 Dep 的 id 已經(jīng)存在,就不再收集該 Watcher 了。

Vue2 源碼中采用的是方案 2 這里我們實(shí)現(xiàn)下:

Dep 類的話只需要引入 id 即可。

/*************改動(dòng)***************************/
let uid = 0;
/****************************************/
export default class Dep {
static target; //當(dāng)前在執(zhí)行的函數(shù)
subs; // 依賴的函數(shù)
id; // Dep 對(duì)象標(biāo)識(shí)
constructor() {
/**************改動(dòng)**************************/
this.id = uid++;
/****************************************/
this.subs = []; // 保存所有需要執(zhí)行的函數(shù)
}

addSub(sub) {
this.subs.push(sub);
}
depend() {
if (Dep.target) {
// 委托給 Dep.target 去調(diào)用 addSub
Dep.target.addDep(this);
}
}

notify() {
for (let i = 0, l = this.subs.length; i < l; i++) {
this.subs[i].update();
}
}
}

Dep.target = null; // 靜態(tài)變量,全局唯一

在 Watcher 中,我們引入 this.depIds 來記錄所有的 id 。

import Dep from "./dep";
export default class Watcher {
constructor(Fn) {
this.getter = Fn;
/*************改動(dòng)***************************/
this.depIds = new Set(); // 擁有 has 函數(shù)可以判斷是否存在某個(gè) id
/****************************************/
this.get();
}

/**
* Evaluate the getter, and re-collect dependencies.
*/
get() {
Dep.target = this; // 保存包裝了當(dāng)前正在執(zhí)行的函數(shù)的 Watcher
let value;
try {
value = this.getter.call();
} catch (e) {
throw e;
} finally {
this.cleanupDeps();
}
return value;
}

/**
* Add a dependency to this directive.
*/
addDep(dep) {
/*************改動(dòng)***************************/
const id = dep.id;
if (!this.depIds.has(id)) {
dep.addSub(this);
}
/****************************************/

}

/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update() {
this.run();
}

/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
run() {
this.get();
}
}

重置

同樣是兩個(gè)方案:

  • 全量式移除,保存 Watcher 所影響的所有 Dep 對(duì)象,當(dāng)重新收集 Watcher 的前,把當(dāng)前 Watcher 從記錄中的所有 Dep 對(duì)象中移除。
  • 增量式移除,重新收集依賴時(shí),用一個(gè)新的變量記錄所有的 Dep 對(duì)象,之后再和舊的 Dep 對(duì)象列表比對(duì),如果新的中沒有,舊的中有,就將當(dāng)前 Watcher 從該 Dep 對(duì)象中移除。

Vue2 中采用的是方案 2,這里也實(shí)現(xiàn)下。

首先是 Dep 類,我們需要提供一個(gè) removeSub 方法。

import { remove } from "./util";
/*
export function remove(arr, item) {
if (arr.length) {
const index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1);
}
}
}
*/
let uid = 0;

export default class Dep {
static target; //當(dāng)前在執(zhí)行的函數(shù)
subs; // 依賴的函數(shù)
id; // Dep 對(duì)象標(biāo)識(shí)
constructor() {
this.id = uid++;
this.subs = []; // 保存所有需要執(zhí)行的函數(shù)
}

addSub(sub) {
this.subs.push(sub);
}
/*************新增************************/
removeSub(sub) {
remove(this.subs, sub);
}
/****************************************/
depend() {
if (Dep.target) {
// 委托給 Dep.target 去調(diào)用 addSub
Dep.target.addDep(this);
}
}

notify() {
for (let i = 0, l = this.subs.length; i < l; i++) {
this.subs[i].update();
}
}
}

Dep.target = null; // 靜態(tài)變量,全局唯一

然后是 Watcher 類,我們引入 this.deps 來保存所有的舊 Dep 對(duì)象,引入 this.newDeps 來保存所有的新 Dep 對(duì)象。

import Dep from "./dep";
export default class Watcher {
constructor(Fn) {
this.getter = Fn;
this.depIds = new Set(); // 擁有 has 函數(shù)可以判斷是否存在某個(gè) id
/*************新增************************/
this.deps = [];
this.newDeps = []; // 記錄新一次的依賴
this.newDepIds = new Set();
/****************************************/
this.get();
}

/**
* Evaluate the getter, and re-collect dependencies.
*/
get() {
Dep.target = this; // 保存包裝了當(dāng)前正在執(zhí)行的函數(shù)的 Watcher
let value;
try {
value = this.getter.call();
} catch (e) {
throw e;
} finally {
/*************新增************************/
this.cleanupDeps();
/****************************************/
}
return value;
}

/**
* Add a dependency to this directive.
*/
addDep(dep) {
const id = dep.id;
/*************新增************************/
// 新的依賴已經(jīng)存在的話,同樣不需要繼續(xù)保存
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id);
this.newDeps.push(dep);
if (!this.depIds.has(id)) {
dep.addSub(this);
}
}
/****************************************/
}

/**
* Clean up for dependency collection.
*/
/*************新增************************/
cleanupDeps() {
let i = this.deps.length;
// 比對(duì)新舊列表,找到舊列表里有,但新列表里沒有,來移除相應(yīng) Watcher
while (i--) {
const dep = this.deps[i];
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this);
}
}

// 新的列表賦值給舊的,新的列表清空
let tmp = this.depIds;
this.depIds = this.newDepIds;
this.newDepIds = tmp;
this.newDepIds.clear();
tmp = this.deps;
this.deps = this.newDeps;
this.newDeps = tmp;
this.newDeps.length = 0;
}
/****************************************/
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update() {
this.run();
}

/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
run() {
this.get();
}
}

測(cè)試

回到開頭的代碼:

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello, world",
ok: true,
};
observe(data);

const updateComponent = () => {
console.log("收到", data.ok ? data.text : "not");
};

new Watcher(updateComponent); // updateComponent 執(zhí)行一次函數(shù),輸出 hello, world

data.ok = false; // updateComponent 執(zhí)行一次函數(shù),輸出 not

data.text = "hello, liang"; // updateComponent 會(huì)執(zhí)行嗎?

此時(shí) data.text 修改的話就不會(huì)再執(zhí)行 updateComponent 了,因?yàn)榈诙螆?zhí)行的時(shí)候,我們把 data.text 中 Dep 里的 Watcher 清除了。

總結(jié)

今天這個(gè)主要就是對(duì)響應(yīng)式系統(tǒng)的一點(diǎn)優(yōu)化,避免不必要的重新執(zhí)行。所做的事情就是重新調(diào)用函數(shù)的時(shí)候,把已經(jīng)沒有關(guān)聯(lián)的 Watcher 去除。

不知道看到這里大家有沒有一個(gè)疑問,我是一直沒想到說服我的點(diǎn),歡迎一起交流:

在解決去重問題上,我們是引入了 id ,但如果直接用 set 其實(shí)就可以。在 Watcher 類中是用 Set 來存 id ,用數(shù)組來存 Dep 對(duì)象,為什么不直接用 Set 來存 Dep 對(duì)象呢?

責(zé)任編輯:武曉燕 來源: windliang
相關(guān)推薦

2022-03-29 09:59:58

響應(yīng)式系統(tǒng)Vue2

2022-04-03 19:27:35

Vue2響應(yīng)式系統(tǒng)

2022-04-06 07:28:47

數(shù)組響應(yīng)式系統(tǒng)

2022-04-02 09:56:41

Vue2響應(yīng)式系統(tǒng)

2022-04-14 08:46:46

響應(yīng)式系統(tǒng)js

2022-04-12 10:05:18

響應(yīng)式系統(tǒng)異步隊(duì)列

2022-04-10 11:04:40

響應(yīng)式系統(tǒng)setdelete

2022-08-31 08:09:35

Vue2AST模版

2024-09-02 16:10:19

vue2前端

2023-03-02 11:51:00

數(shù)據(jù)分析師企業(yè)

2024-03-07 12:54:06

數(shù)據(jù)分析師企業(yè)

2019-04-25 14:20:56

數(shù)據(jù)分析套路工具

2021-05-19 14:25:19

前端開發(fā)技術(shù)

2022-06-26 00:00:02

Vue3響應(yīng)式系統(tǒng)

2024-03-15 11:47:19

Vue2前端權(quán)限控制

2023-02-13 00:20:08

分布式系統(tǒng)安全

2023-11-19 18:53:27

Vue2MVVM

2016-10-19 20:47:55

vuevue-cli移動(dòng)端

2020-09-25 07:40:39

技術(shù)開發(fā)選型

2019-12-06 10:44:53

Vue 3.0響應(yīng)式系統(tǒng)前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: www.国产精品 | 亚洲免费成人av | 精品国产黄a∨片高清在线 成人区精品一区二区婷婷 日本一区二区视频 | 国产精品久久久久久久久久 | 欧美天堂| 成人一区精品 | 精品精品视频 | 91在线电影 | 欧美日韩国产不卡 | 天天躁天天操 | 在线一区二区观看 | 日本久久精品 | 毛片免费观看 | 亚洲一区不卡在线 | 成人精品一区二区三区中文字幕 | 日韩精品人成在线播放 | 亚洲午夜精品久久久久久app | 在线观看av网站永久 | 久草在线 | 亚洲一区二区在线电影 | 久久精品一区二区三区四区 | 99福利视频 | 欧美最猛黑人xxxⅹ 粉嫩一区二区三区四区公司1 | 一区二区三区亚洲 | 欧美日韩国产精品一区 | 久久久久久久久91 | 国产精品久久久久久久久久了 | 亚洲视频一区二区三区四区 | 国产精品美女久久久 | a在线视频| 成人av片在线观看 | 91高清在线观看 | 国产精品成人av | 日本精品网站 | 精品久久久久久亚洲精品 | 成人在线一区二区 | 日韩精品一区二区三区在线观看 | 久久成人一区 | 亚洲欧美日韩高清 | 日本精品一区二区三区在线观看 | 春色av|