Vue2和Vue3的雙向數(shù)據(jù)綁定原理分析
vue2.x 是如何實(shí)現(xiàn)響應(yīng)式系統(tǒng)的
當(dāng)你把一個普通的 js 對象傳入 vue 實(shí)例作為 data 選項(xiàng),vue 將遍歷此對象的所有prototype(屬性),并使用 object.defineProperty(),將這些 prototype(屬性),全部轉(zhuǎn)換為 getter / setter,在 getter 中收集數(shù)據(jù)依賴,在 setter 中監(jiān)聽數(shù)據(jù)變化,一旦數(shù)據(jù)發(fā)生改變,在通知訂閱者。
每個組件實(shí)例,都對應(yīng)一個 watcher 實(shí)例,它會在組件渲染的過程把 “接觸” 過的數(shù)據(jù) prototype(屬性)記錄為依賴,之后當(dāng)依賴項(xiàng)的 setter 觸發(fā)時,會通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染;
defineProperty 的痛點(diǎn)
- 它無法發(fā)現(xiàn)對象新增和被刪除的屬性,當(dāng)你給一個對象添加一個新的屬性,這個新增的屬性沒有添加到 vue 的數(shù)據(jù)更新偵查機(jī)制里;
Vue.set() 可以讓 vue 知道你新增了一個屬性,其實(shí) Vue.set 可以讓 Vue 知道新增了一個屬性。其實(shí) set() 內(nèi)部也是通過調(diào)用 defineProperty() 來實(shí)現(xiàn);
- 當(dāng)你利用索引直接設(shè)置一個數(shù)組(new Array(4))或者修改數(shù)組的長度時,Vue 不能檢測到數(shù)組的變動;
- 當(dāng)對象嵌套的層數(shù)特別深(多層嵌套)的時候,遞歸遍歷帶來的性能開銷就會比較大;
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)置了攔截器對象。所有的外部訪問都得先經(jīng)過這一層攔截,不管是先前定義好的,還是新增的屬性,又或者是深層的嵌套屬性,訪問時都會被攔截;
Reflect.set()方法用于設(shè)置對象屬性的值,1:目標(biāo)對象:2:改變參數(shù)的名稱:3:改變參數(shù)的值:4:值是this如果遇到設(shè)置器,將提供給目標(biāo)調(diào)用。
此方法返回一個布爾值,該值指示該屬性是否已成功設(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è)置對象屬性的值:1:目標(biāo)對象:2:改變參數(shù)的名稱:3:改變參數(shù)的值 // 此方法返回一個布爾值,該值指示該屬性是否已成功設(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è)置對象屬性的值,1:目標(biāo)對象:2:改變參數(shù)的名稱:3:改變參數(shù)的值:4:值是this如果遇到設(shè)置器,將提供給目標(biāo)調(diào)用。 // 此方法返回一個布爾值,該值指示該屬性是否已成功設(shè)置。 return Reflect.set(target, propKey, value, receiver); }, }); p.age = "20"; console.log(p); p.newProperty = "New attribute"; console.log(p); },
結(jié)果:
總結(jié)
- proxy 是用來操作對象并且擴(kuò)展對象能到,而 Object.defineProperty() 只是單純的操作對象的屬性;
- Vue2.X 是用 Object.defineProperty() 實(shí)現(xiàn)數(shù)據(jù)響應(yīng)的,但是受限于 defineProperty() 的實(shí)現(xiàn),必須遞歸遍歷至對象的最底層;
- vue3.0 用 Proxy 來攔截對象,不管對目標(biāo)執(zhí)行任何操作,都會先通過 Proxy 的處理邏輯;
- 除了 Vue3.0,還有其他的庫也在使用 Proxy。
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue點(diǎn)擊標(biāo)簽切換選中及互相排斥操作
這篇文章主要介紹了vue點(diǎn)擊標(biāo)簽切換選中及互相排斥操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07vue elementUI Plus實(shí)現(xiàn)拖拽流程圖的詳細(xì)代碼(不引入插件)
文章介紹了如何使用Vue和elementUI Plus實(shí)現(xiàn)一個簡單的拖拽流程圖功能,不引入任何插件,完全手寫,設(shè)計(jì)思路,感興趣的朋友跟隨小編一起看看吧2025-01-01vue實(shí)現(xiàn)給某個數(shù)據(jù)字段添加顏色
這篇文章主要介紹了vue實(shí)現(xiàn)給某個數(shù)據(jù)字段添加顏色方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03vue router點(diǎn)擊打開新的標(biāo)簽頁的方法(最新推薦)
vue router點(diǎn)擊打開新的標(biāo)簽頁的方法,只需要在router-link中加入target="_blank"即可在新的頁面打開標(biāo)簽,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-10-10