日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Vue2剝絲抽繭-響應(yīng)式系統(tǒng)之Set和的Delete

數(shù)組set

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
list: [1, 2],
};
observe(data);
const updateComponent = () => {
console.log(data.list);
};

new Watcher(updateComponent);

list[0] = 3;

可以先思考一下。

... ...

答案是否定的,數(shù)組我們只能通過(guò)重寫的 push、 splice 等方法去觸發(fā)更新,詳見 Vue2剝絲抽繭-響應(yīng)式系統(tǒng)之?dāng)?shù)組。

如果我們想要替換數(shù)組某個(gè)元素的話可以轉(zhuǎn)一下彎,通過(guò) splice 去實(shí)現(xiàn)。


import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
list: [1, 2],
};
observe(data);
const updateComponent = () => {
console.log(data.list);
};

new Watcher(updateComponent);

// list[0] = 3;
data.list.splice(0, 1, 3);

每次這樣寫太麻煩了,我們可以提供一個(gè) set 方法供用戶使用。

/**
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
*/
export function set(target, key, val) {
if (Array.isArray(target)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val;
}

// targe 是對(duì)象的情況
// ...
}

然后我們直接使用 set 方法就可以了。

import { observe, set } from "./reactive";
import Watcher from "./watcher";
const data = {
list: [1, 2],
};
observe(data);
const updateComponent = () => {
console.log(data.list);
};

new Watcher(updateComponent);

// list[0] = 3;
// data.list.splice(0, 1, 3);
set(data.list, 0, 3);

數(shù)組 del

同數(shù)組 set ,我們順便提供一個(gè) del 的方法,支持?jǐn)?shù)組響應(yīng)式的刪除某個(gè)元素。

/**
* Delete a property and trigger change if necessary.
*/
export function del(target, key) {
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1);
return;
}
// targe 是對(duì)象的情況
// ...
}

對(duì)象 set

import { observe, set, del } from "./reactive";
import Watcher from "./watcher";
const data = {
obj: {
a: 1,
b: 2,
},
};
observe(data);
const updateComponent = () => {
const c = data.obj.c ? data.obj.c : 0;
console.log(data.obj.a + data.obj.b + c);
};

new Watcher(updateComponent);

data.obj.c = 3;

updateComponent 方法中雖然使用了 obj 的 c 屬性,但是在調(diào)用 observe 之前,data.obj 中并沒有 c 屬性,所以 c 屬性不是響應(yīng)式的。

當(dāng)我們修改 data.obj.c 的值的時(shí)候,并不會(huì)觸發(fā) updateComponent 的執(zhí)行。

如果想要變成響應(yīng)式的話,一種方法就是在最開始就定義 c 屬性。

const data = {
obj: {
a: 1,
b: 2,
c: null,
},
};
observe(data);
const updateComponent = () => {
const c = data.obj.c ? data.obj.c : 0;
console.log(data.obj.a + data.obj.b + c);
};

new Watcher(updateComponent);

data.obj.c = 3;

另一種方法就是通過(guò) set 去設(shè)置新的屬性了,在 set 中我們可以將新添加的屬性設(shè)置為響應(yīng)式的。

/**
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
*/
export function set(target, key, val) {
if (Array.isArray(target)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val;
}

// targe 是對(duì)象的情況
// key 在 target 中已經(jīng)存在
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}

const ob = target.__ob__;
// target 不是響應(yīng)式數(shù)據(jù)
if (!ob) {
target[key] = val;
return val;
}
// 將當(dāng)前 key 變?yōu)轫憫?yīng)式的
defineReactive(target, key, val);
return val;
}

回到我們之前的程序:

import { observe, set, del } from "./reactive";
import Watcher from "./watcher";
const data = {
obj: {
a: 1,
b: 2,
},
};
observe(data);
const updateComponent = () => {
const c = data.obj.c ? data.obj.c : 0;
console.log(data.obj.a + data.obj.b + c);
};

const ob = new Watcher(updateComponent);

set(data.obj, "c", 3);

雖然通過(guò) set 增加了屬性,但是此時(shí) Watcher 并不會(huì)重新觸發(fā),原因的話我們看下依賴圖。

雖然屬性 c 擁有了 Dep 對(duì)象,但由于沒有調(diào)用過(guò)依賴屬性 c 的 Watcher ,所以它并沒有收集到依賴。

當(dāng)然我們可以 set 完手動(dòng)調(diào)用一次相應(yīng)的 Watcher 。

const data = {
obj: {
a: 1,
b: 2,
},
};
observe(data);
const updateComponent = () => {
const c = data.obj.c ? data.obj.c : 0;
console.log(data.obj.a + data.obj.b + c);
};

const ob = new Watcher(updateComponent);

set(data.obj, "c", 3);
ob.update(); // 手動(dòng)調(diào)用 Watcher

data.obj.c = 4;

這樣的話,當(dāng)執(zhí)行 data.obj.c = 4 的時(shí)候就會(huì)觸發(fā) Watcher 的執(zhí)行了。

那么我們能將觸發(fā)相應(yīng)的 Watcher 的邏輯放到 set 函數(shù)中嗎?

可以看到 obj 里也有個(gè) Dep,這個(gè)其實(shí)當(dāng)時(shí)是為數(shù)組準(zhǔn)備的,參考 Vue2剝絲抽繭-響應(yīng)式系統(tǒng)之?dāng)?shù)組,但 obj 的 dep 什么都沒收集。

我們修改一下代碼讓它也收集一下:

export function defineReactive(obj, key, val, shallow) {
const property = Object.getOwnPropertyDescriptor(obj, key);
// 讀取用戶可能自己定義了的 get、set
const getter = property && property.get;
const setter = property && property.set;
// val 沒有傳進(jìn)來(lái)話進(jìn)行手動(dòng)賦值
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
const dep = new Dep(); // 持有一個(gè) Dep 對(duì)象,用來(lái)保存所有依賴于該變量的 Watcher
let childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
/******新位置 *************************/
childOb.dep.depend();
/**********************************/
if (Array.isArray(value)) {
// childOb.dep.depend(); //原來(lái)的位置
dependArray(value);
}
}
}
return value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;

if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
},
});
}

function dependArray(value) {
for (let e, i = 0, l = value.length; i < l; i++) {
e = value[i];
/******新位置 *************************/
e && e.__ob__ && e.__ob__.dep.depend();
/**********************************/
if (Array.isArray(e)) {
// e && e.__ob__ && e.__ob__.dep.depend(); // 原位置
dependArray(e);
}
}
}

因?yàn)樽x取 a 屬性,一定先會(huì)讀取 obj 屬性,即 data.obj.a。b 也同理。

所以通過(guò)上邊的修改,obj 的 dep 會(huì)收集到它的所有屬性的依賴,也就是這里的 a、b 的依賴,但因?yàn)?a 和 b 的依賴是相同的,所以收集到一個(gè)依賴。

但其實(shí)我們并不知道 c 被哪些 Watcher 依賴,我們只知道和 c 同屬于一個(gè)對(duì)象的 a 和 b 被哪些 Watcher 依賴,但大概率 c 也會(huì)被其中的 Watcher 依賴。

所以我們可以在 set 中手動(dòng)執(zhí)行一下 obj 的 Dep ,依賴 c 的 Watcher 大概率會(huì)被執(zhí)行,相應(yīng)的 c 也會(huì)成功收集到依賴。

export function set(target, key, val) {
if (Array.isArray(target)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val;
}

// targe 是對(duì)象的情況
// key 在 target 中已經(jīng)存在
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}

const ob = target.__ob__;
// target 不是響應(yīng)式數(shù)據(jù)
if (!ob) {
target[key] = val;
return val;
}
defineReactive(target, key, val);
/******新增 *************************/
ob.dep.notify()
/************************************/
return val;
}

回到最開始的代碼:

const data = {
obj: {
a: 1,
b: 2,
},
};
observe(data);
const updateComponent = () => {
const c = data.obj.c ? data.obj.c : 0;
console.log(data.obj.a + data.obj.b + c);
};

const ob = new Watcher(updateComponent);

set(data.obj, "c", 3);

執(zhí)行完后 c 除了變?yōu)轫憫?yīng)式的,也成功觸發(fā)了 Watcher 執(zhí)行,并且收集到了 Watcher。

此時(shí)如果修改 c 的值,也會(huì)成功觸發(fā) Watcher 的執(zhí)行了。

對(duì)象 del有了上邊的了解,刪除就很好解決了。

如果要?jiǎng)h除 a 屬性,刪除后執(zhí)行下它相應(yīng)的 Dep 就可以。但 a 的 Dep 是存在閉包中的,我們并不能拿到。

退而求其次,我們可以去執(zhí)行 a 屬性所在的對(duì)象 obj 的 Dep 就可以了。

/**
* Delete a property and trigger change if necessary.
*/
export function del(target, key) {
if (Array.isArray(target)) {
target.splice(key, 1);
return;
}
// targe 是對(duì)象的情況
const ob = target.__ob__;
if (!hasOwn(target, key)) {
return;
}
delete target[key];
if (!ob) {
return;
}
ob.dep.notify();
}

總結(jié)

通過(guò)為對(duì)象收集依賴,將對(duì)象、數(shù)組的修改、刪除也變成響應(yīng)式的了,同時(shí)為用戶提供了 set 和 del 方法。


文章題目:Vue2剝絲抽繭-響應(yīng)式系統(tǒng)之Set和的Delete
轉(zhuǎn)載注明:http://www.dlmjj.cn/article/djoggpj.html