Vue2響應(yīng)式系統(tǒng)之set和delete
1、數(shù)組集
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;
list[0]會(huì)觸發(fā) 的重新執(zhí)行嗎?updateComponent
可以先思考一下。
答案是否定的,數(shù)組我們只能通過(guò)重寫(xiě)的 、 等方法去觸發(fā)更新,詳見(jiàn)pushspliceVue2響應(yīng)式系統(tǒng)之?dāng)?shù)組 。
如果我們想要替換數(shù)組某個(gè)元素的話可以轉(zhuǎn)一下彎,通過(guò) 去實(shí)現(xiàn)。splice
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);
每次這樣寫(xiě)太麻煩了,我們可以提供一個(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);
2、數(shù)組 del
同數(shù)組 ,我們順便提供一個(gè) 的方法,支持?jǐn)?shù)組響應(yīng)式的刪除某個(gè)元素。setdel
/**
* 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ì)象的情況
// ...
}
3、對(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 方法中雖然使用了 的 屬性,但是在調(diào)用 之前, 中并沒(méi)有 屬性,所以 屬性不是響應(yīng)式的。objcobservedata.objcc
當(dāng)我們修改 的值的時(shí)候,并不會(huì)觸發(fā) 的執(zhí)行。data.obj.cupdateComponent
如果想要變成響應(yīng)式的話,一種方法就是在最開(kāi)始就定義 屬性。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ò) 去設(shè)置新的屬性了,在 中我們可以將新添加的屬性設(shè)置為響應(yīng)式的。setset
/**
* 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ò) 增加了屬性,但是此時(shí) 并不會(huì)重新觸發(fā),原因的話我們看下依賴(lài)圖。setWatcher

雖然屬性 擁有了 對(duì)象,但由于沒(méi)有調(diào)用過(guò)依賴(lài)屬性 的 ,所以它并沒(méi)有收集到依賴(lài)。cDepcWatcher
當(dāng)然我們可以 完手動(dòng)調(diào)用一次相應(yīng)的 。setWatcher
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í)行 的時(shí)候就會(huì)觸發(fā) 的執(zhí)行了。data.obj.c = 4Watcher
那么我們能將觸發(fā)相應(yīng)的 的邏輯放到 函數(shù)中嗎?Watcherset

可以看到 里也有個(gè) ,這個(gè)其實(shí)當(dāng)時(shí)是為數(shù)組準(zhǔn)備的,參考 objDepVue2響應(yīng)式系統(tǒng)之?dāng)?shù)組,但 的 什么都沒(méi)收集。objdep
我們修改一下代碼讓它也收集一下:
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 沒(méi)有傳進(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)保存所有依賴(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取 屬性,一定先會(huì)讀取 屬性,即 。 也同理。aobjdata.obj.ab
所以通過(guò)上邊的修改, 的 會(huì)收集到它的所有屬性的依賴(lài),也就是這里的 、 的依賴(lài),但因?yàn)?nbsp;和 的依賴(lài)是相同的,所以收集到一個(gè)依賴(lài)。objdepabab

但其實(shí)我們并不知道 被哪些 依賴(lài),我們只知道和 同屬于一個(gè)對(duì)象的 和 被哪些 依賴(lài),但大概率 也會(huì)被其中的 依賴(lài)。cWatchercabWatchercWatcher
所以我們可以在 中手動(dòng)執(zhí)行一下 的 ,依賴(lài) 的 大概率會(huì)被執(zhí)行,相應(yīng)的 也會(huì)成功收集到依賴(lài)。setobjDepcWatcherc
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;
}
回到最開(kāi)始的代碼:
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í)行完后 除了變?yōu)轫憫?yīng)式的,也成功觸發(fā)了 執(zhí)行,并且收集到了 。cWatcherWatcher

此時(shí)如果修改 的值,也會(huì)成功觸發(fā) 的執(zhí)行了。cWatcher
4、對(duì)象 del
有了上邊的了解,刪除就很好解決了。

如果要?jiǎng)h除 屬性,刪除后執(zhí)行下它相應(yīng)的 就可以。但 的 是存在閉包中的,我們并不能拿到。aDepaDep
退而求其次,我們可以去執(zhí)行 屬性所在的對(duì)象 的 就可以了。aobjDep
/**
* 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();
}
5、總結(jié)
通過(guò)為對(duì)象收集依賴(lài),將對(duì)象、數(shù)組的修改、刪除也變成響應(yīng)式的了,同時(shí)為用戶提供了 和 方法。
到此這篇關(guān)于Vue2響應(yīng)式系統(tǒng)之set和delete的文章就介紹到這了,更多相關(guān)Vue2 set和delete內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡(jiǎn)單談?wù)刅ue 模板各類(lèi)數(shù)據(jù)綁定
Vue.js 的模板是基于 DOM 實(shí)現(xiàn)的。這意味著所有的 Vue.js 模板都是可解析的有效的 HTML,且通過(guò)一些特殊的特性做了增強(qiáng)。Vue 模板因而從根本上不同于基于字符串的模板,請(qǐng)記住這點(diǎn)。2016-09-09
如何利用vue+vue-router+elementUI實(shí)現(xiàn)簡(jiǎn)易通訊錄
這篇文章主要介紹了如何利用vue+vue-router+elementUI實(shí)現(xiàn)簡(jiǎn)易通訊錄,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05
手把手教學(xué)vue的路由權(quán)限問(wèn)題
這篇文章主要介紹了手把手教學(xué)vue的路由權(quán)限問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
vue elementui form表單驗(yàn)證的實(shí)現(xiàn)
這篇文章主要介紹了vue elementui form表單驗(yàn)證的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
vue 項(xiàng)目中實(shí)現(xiàn)按鈕防抖方法
這篇文章主要介紹了vue 項(xiàng)目中實(shí)現(xiàn)按鈕防抖方法,首先需要新建 .js文件存放防抖方法,引入防抖文件,methods中添加方法,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
解決vuex數(shù)據(jù)頁(yè)面刷新后初始化操作
這篇文章主要介紹了解決vuex數(shù)據(jù)頁(yè)面刷新后初始化操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
Vue中通過(guò)vue-router實(shí)現(xiàn)命名視圖的問(wèn)題
這篇文章主要介紹了在Vue中通過(guò)vue-router實(shí)現(xiàn)命名視圖,本文給大家提到了vue-router的原理解析,給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
vue2 設(shè)置router-view默認(rèn)路徑的實(shí)例
今天小編就為大家分享一篇vue2 設(shè)置router-view默認(rèn)路徑的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
使用vue與jquery實(shí)時(shí)監(jiān)聽(tīng)用戶輸入狀態(tài)的操作代碼
本文是腳本之家小編給大家?guī)?lái)的使用vue與jquery實(shí)時(shí)監(jiān)聽(tīng)用戶輸入狀態(tài),實(shí)現(xiàn)效果是input未輸入值時(shí),按鈕禁用狀態(tài),具體操作代碼大家參考下本文吧2017-09-09

