Vue2和Vue3的雙向數(shù)據(jù)綁定原理分析
vue2.x 是如何實(shí)現(xiàn)響應(yīng)式系統(tǒng)的
當(dāng)你把一個(gè)普通的 js 對(duì)象傳入 vue 實(shí)例作為 data 選項(xiàng),vue 將遍歷此對(duì)象的所有prototype(屬性),并使用 object.defineProperty(),將這些 prototype(屬性),全部轉(zhuǎn)換為 getter / setter,在 getter 中收集數(shù)據(jù)依賴,在 setter 中監(jiān)聽數(shù)據(jù)變化,一旦數(shù)據(jù)發(fā)生改變,在通知訂閱者。
每個(gè)組件實(shí)例,都對(duì)應(yīng)一個(gè) watcher 實(shí)例,它會(huì)在組件渲染的過程把 “接觸” 過的數(shù)據(jù) prototype(屬性)記錄為依賴,之后當(dāng)依賴項(xiàng)的 setter 觸發(fā)時(shí),會(huì)通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染;
defineProperty 的痛點(diǎn)
- 它無法發(fā)現(xiàn)對(duì)象新增和被刪除的屬性,當(dāng)你給一個(gè)對(duì)象添加一個(gè)新的屬性,這個(gè)新增的屬性沒有添加到 vue 的數(shù)據(jù)更新偵查機(jī)制里;
Vue.set() 可以讓 vue 知道你新增了一個(gè)屬性,其實(shí) Vue.set 可以讓 Vue 知道新增了一個(gè)屬性。其實(shí) set() 內(nèi)部也是通過調(diào)用 defineProperty() 來實(shí)現(xiàn);
- 當(dāng)你利用索引直接設(shè)置一個(gè)數(shù)組(new Array(4))或者修改數(shù)組的長(zhǎng)度時(shí),Vue 不能檢測(cè)到數(shù)組的變動(dòng);
- 當(dāng)對(duì)象嵌套的層數(shù)特別深(多層嵌套)的時(shí)候,遞歸遍歷帶來的性能開銷就會(huì)比較大;
Object.defineProperty 代碼的使用
mounted() {
// 先定義好一套規(guī)則
class Observer {
constructor(data) {
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("You visited" + key);
return data[key];
},
set(NewValue) {
console.log("You set" + key);
console.log("New Value" + NewValue);
if (NewValue === data[key]) {
return;
}
data[key] = NewValue;
},
});
}
}
}
let obj = {
name: "app",
age: 18,
a: {
b: 1,
c: {
d: 1,
},
},
};
let app = new Observer(obj);
console.log(app);
app.age = 20;
app.newProperty = "new attrs";
console.log(app);
},結(jié)果:

Proxy 方法的理解
Proxy 在 vue3.0 中上位
可以解決 defineProperty 的痛點(diǎn),因?yàn)楸举|(zhì)的原因在于 Proxy 是內(nèi)置了攔截器對(duì)象。所有的外部訪問都得先經(jīng)過這一層攔截,不管是先前定義好的,還是新增的屬性,又或者是深層的嵌套屬性,訪問時(shí)都會(huì)被攔截;
Reflect.set()方法用于設(shè)置對(duì)象屬性的值,1:目標(biāo)對(duì)象:2:改變參數(shù)的名稱:3:改變參數(shù)的值:4:值是this如果遇到設(shè)置器,將提供給目標(biāo)調(diào)用。
此方法返回一個(gè)布爾值,該值指示該屬性是否已成功設(shè)置。
Proxy 代碼的使用
mounted() {
const obj = {
name: "app",
age: 19,
a: {
b: 1,
c: 2,
},
};
const p = new Proxy(obj, {
get(target, propKey, receiver) {
console.log("Your visited:" + propKey);
// Reflect.set()方法用于設(shè)置對(duì)象屬性的值:1:目標(biāo)對(duì)象:2:改變參數(shù)的名稱:3:改變參數(shù)的值
// 此方法返回一個(gè)布爾值,該值指示該屬性是否已成功設(shè)置。
return Reflect.set(target, propKey, receiver);
},
set(target, propKey, value, receiver) {
console.log("You set:" + propKey);
console.log("New value:" + value);
// Reflect.set()方法用于設(shè)置對(duì)象屬性的值,1:目標(biāo)對(duì)象:2:改變參數(shù)的名稱:3:改變參數(shù)的值:4:值是this如果遇到設(shè)置器,將提供給目標(biāo)調(diào)用。
// 此方法返回一個(gè)布爾值,該值指示該屬性是否已成功設(shè)置。
return Reflect.set(target, propKey, value, receiver);
},
});
p.age = "20";
console.log(p);
p.newProperty = "New attribute";
console.log(p);
},結(jié)果:

總結(jié)
- proxy 是用來操作對(duì)象并且擴(kuò)展對(duì)象能到,而 Object.defineProperty() 只是單純的操作對(duì)象的屬性;
- Vue2.X 是用 Object.defineProperty() 實(shí)現(xiàn)數(shù)據(jù)響應(yīng)的,但是受限于 defineProperty() 的實(shí)現(xiàn),必須遞歸遍歷至對(duì)象的最底層;
- vue3.0 用 Proxy 來攔截對(duì)象,不管對(duì)目標(biāo)執(zhí)行任何操作,都會(huì)先通過 Proxy 的處理邏輯;
- 除了 Vue3.0,還有其他的庫也在使用 Proxy。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于vue的短信驗(yàn)證碼倒計(jì)時(shí)demo
這篇文章主要介紹了基于vue的短信驗(yàn)證碼倒計(jì)時(shí)demo,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
Vue.js實(shí)現(xiàn)高質(zhì)量翻頁功能的完整開發(fā)指南
當(dāng)我們?cè)诰W(wǎng)頁中展示大量數(shù)據(jù)時(shí),分頁能幫助用戶快速瀏覽內(nèi)容,提高頁面的加載性能和用戶體驗(yàn),本文將從基礎(chǔ)翻頁功能入手,逐步升級(jí)至可復(fù)用的分頁組件,并提供性能優(yōu)化和用戶體驗(yàn)提升的實(shí)用建議,需要的朋友可以參考下2025-07-07
Vue+Element-ui彈窗?this.$alert?is?not?a?function問題
這篇文章主要介紹了Vue+Element-ui彈窗?this.$alert?is?not?a?function問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
vuex 動(dòng)態(tài)注冊(cè)方法 registerModule的實(shí)現(xiàn)
這篇文章主要介紹了vuex 動(dòng)態(tài)注冊(cè)方法 registerModule的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
Vue點(diǎn)擊在彈窗外部實(shí)現(xiàn)一鍵關(guān)閉的示例代碼
在Vue應(yīng)用中,彈窗是一個(gè)常見的交互元素,有時(shí)我們可能希望用戶點(diǎn)擊彈窗外部時(shí),彈窗能夠自動(dòng)關(guān)閉,本文主要介紹了Vue點(diǎn)擊在彈窗外部實(shí)現(xiàn)一鍵關(guān)閉的示例代碼,感興趣的可以了解一下2024-06-06

