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

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

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

場景

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

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 會執(zhí)行嗎?

我們來一步一步理清:

observer(data)

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

new Watcher(updateComponent);

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

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

data.ok = false;

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

執(zhí)行 updateComponent 就會重新讀取 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 ,所以就不會觸發(fā) data.text 的 get ,text 的 Dep 就不會變化了。

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

data.text = "hello, liang";

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

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

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

問題

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

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

去重

去重的話有兩種方案:

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

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

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

/*************改動***************************/
let uid = 0;
/****************************************/
export default class Dep {
static target; //當(dāng)前在執(zhí)行的函數(shù)
subs; // 依賴的函數(shù)
id; // Dep 對象標(biāo)識
constructor() {
/**************改動**************************/
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;
/*************改動***************************/
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) {
/*************改動***************************/
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 對象,當(dāng)重新收集 Watcher 的前,把當(dāng)前 Watcher 從記錄中的所有 Dep 對象中移除。
  • 增量式移除,重新收集依賴時(shí),用一個(gè)新的變量記錄所有的 Dep 對象,之后再和舊的 Dep 對象列表比對,如果新的中沒有,舊的中有,就將當(dāng)前 Watcher 從該 Dep 對象中移除。

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 對象標(biāo)識
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 對象,引入 this.newDeps 來保存所有的新 Dep 對象。

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;
// 比對新舊列表,找到舊列表里有,但新列表里沒有,來移除相應(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();
}
}

測試

回到開頭的代碼:

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 會執(zhí)行嗎?

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

總結(jié)

今天這個(gè)主要就是對響應(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 對象,為什么不直接用 Set 來存 Dep 對象呢?

責(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模版

2023-03-02 11:51:00

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

2024-03-07 12:54:06

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

2024-09-02 16:10:19

vue2前端

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移動端

2020-09-25 07:40:39

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

2020-06-09 11:35:30

Vue 3響應(yīng)式前端
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 国产精品久久久久久模特 | 国产在线资源 | 国产乱码精品1区2区3区 | 免费亚洲网站 | 精品久久久久久久久久久久久 | 亚洲 中文 欧美 日韩 在线观看 | 国产精品久久久久久婷婷天堂 | 国产资源在线播放 | 日韩在线免费视频 | 在线中文字幕亚洲 | 91在线免费视频 | 亚洲高清视频在线 | 91视频在线 | 久久爱一区 | 久久久精品一区二区三区 | 免费黄色大片 | 日韩伦理一区二区 | 国产91丝袜| 日韩不卡在线观看 | 免费一区| 一区观看 | 欧美成人在线影院 | 欧美成人激情视频 | 亚欧性视频 | 亚洲网站在线观看 | 美女国产 | 亚洲精品视频一区 | 亚洲国产aⅴ成人精品无吗 亚洲精品久久久一区二区三区 | 国产精品揄拍一区二区久久国内亚洲精 | av成年人网站 | 免费在线视频精品 | 免费在线观看91 | 亚洲精品中文字幕在线观看 | 成人字幕网zmw | 亚洲人成人一区二区在线观看 | 欧美一级免费看 | 国产精品视频网 | 久久亚洲综合 | 久久国产精品72免费观看 | 日韩国产高清在线观看 | 亚洲精品66 |