更新時間:2020-08-13 來源:黑馬程序員 瀏覽量:
vue3 響應(yīng)式數(shù)據(jù)放棄 Object.defineProperty,而使用Proxy來代替它我們知道,在 vue2 中,實現(xiàn)數(shù)據(jù)監(jiān)聽是使用Object.defineProperty--> 實現(xiàn)方法可看:
vue 數(shù)據(jù)雙向綁定原理,而這個方法有缺點,并且不能實現(xiàn)數(shù)組和對象的部分監(jiān)聽情況;具體也可以看我之前寫的一篇博客: 關(guān)于 Vue 不能 watch 數(shù)組和對象變化的解決方案,最新的 Proxy,相比 vue2 的
Object.defineProperty,能達到速度加倍、內(nèi)存減半的成效。具體是怎么實現(xiàn)、以及對比舊的實現(xiàn)方法為啥能有速度加倍、內(nèi)存減半的特性,下面我們來聊聊:
Vue 初始化過程
Vue 的初始化過程,分別有Observer、Compiler和Watcher,當(dāng)我們 new Vue 的時候,會調(diào)用Observer,通過
Object.defineProperty 遍歷 vue 對象的 data、computed 或者
props(如果是組件的話)的所有屬性進行監(jiān)聽。同時通過Compiler解析模板指令,解析到屬性后就 new 一個Watcher并綁定更新函數(shù)到 watcher
當(dāng)中,Observer 和 Compiler 就通過屬性來進行關(guān)聯(lián)。
Object.defineProperty
來個簡單的 Object.defineProperty 例子
class Observer {
constructor(data) {
// 遍歷參數(shù) data 的屬性,給添加到 this 上
for (let key of Object.keys(data)) {
if (typeof data[key] === "object") {
data[key] = new Observer(data[key]);
}
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get() {
console.log("你訪問了" + key);//你訪問了age
return data[key];//20
},
set(newVal) {
console.log("你設(shè)置了" + key); //你設(shè)置了age
console.log("新的" + key + "=" + newVal); //新的age=20
if (newVal === data[key]) {
return;
}
data[key] = newVal;
}
});
}
}
}
const obj = {
name: "app",
age: "18",
a: {
b: 1,
c: 2,
}
};
const app = new Observer(obj);
app.age = 20;
console.log(app.age);
app.newPropKey = "新屬性";
console.log(app.newPropKey); //新屬性從上面可以知道:Object.defineProperty 需要遍歷所有的屬性,這就造成了如果 vue 對象的data/computed/props
中的數(shù)據(jù)規(guī)模龐大,那么遍歷起來就會慢很多.同理,如果 vue 對象的 data/computed/props 中的數(shù)據(jù)規(guī)模龐大,那么
Object.defineProperty 需要監(jiān)聽所有的屬性的變化,那么占用內(nèi)存就會很大。
Proxy
我們來看看proxy
Proxy 對象用于定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數(shù)調(diào)用等)
可以理解為在對象之前設(shè)置一個”攔截“,當(dāng)監(jiān)聽的對象被訪問的時候,都必須經(jīng)過這層攔截??梢栽谶@攔截中對原對象處理,返回需要的數(shù)據(jù)格式,也就是無論訪問對象的什么屬性,之前定義的或是新增的屬性,都會走到攔截中進行處理。這就解決了之前所無法監(jiān)聽的問題。
const obj = {
name: "krry",
age: 24,
others: {
mobile: "mi10",
watch: "mi4",
},
};
const p = new Proxy(obj, {
get(target, key, receiver) {
console.log("查看的屬性為:" + key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log("設(shè)置的屬性為:" + key);
console.log("新的屬性:" + key, "值為:" + value);
Reflect.set(target, key, value, receiver);
},
});
p.age = 22;
console.log(p.age);
p.single = "NO";
console.log(p.single);
p.others.shoe = "boost";
console.log(p.others.shoe);
輸出結(jié)果為:
設(shè)置的屬性為:age
新的屬性:age 值為:22
查看的屬性為:age22
設(shè)置的屬性為:single
新的屬性:single 值為:NO
查看的屬性為:single
NO
查看的屬性為:others
查看的屬性為:others
boost由上可知,新增或編輯屬性,并不需要重新添加響應(yīng)式處理,都能監(jiān)聽的到,因為 Proxy 是對對象的操作,只要你訪問對象,就會走到 Proxy 的邏輯中。Reflect 是一個內(nèi)置的對象,它提供攔截 JavaScript 操作的方法。這些方法與proxy handlers的方法相同。Reflect不是一個函數(shù)對象,因此它是不可構(gòu)造的,區(qū)別Proxy 和 Object.defineProperty 的使用方法看似很相似,其實 Proxy 是在更高維度上去攔截屬性的。
Object.definePropertyVue2 中,對于給定的 data:如 { count: 1 },是需要根據(jù)具體的 key 也就是
count,去對 get 和 set 進行攔截,也就是:
Object.defineProperty(data, 'count', {
get() {},
set() {},
})必須預(yù)先知道要攔截的 key 是什么,這也就是為什么 Vue2 里對于對象上的新增屬性無能為力,所以 Vue 初始化的過程中需要遍歷 data 來挾持數(shù)據(jù)變化,造成速度變慢,內(nèi)存變大的原因。
Proxy 而 Vue3 所使用的 Proxy,則是這樣攔截的:
new Proxy(data, {
get(key) { },
set(key, value) { },
})可以看到,proxy 不需要關(guān)心具體的 key,它去攔截的是修改 data 上的任意 key和讀取 data 上的任意 key
所以,不管是已有的 key 還是新增的 key,都會監(jiān)聽到。
猜你喜歡:
web前端培訓(xùn)課程