Vue3使用Proxy實現數據監聽的原因分析
vue 數據雙向綁定原理,而這個方法有缺點,並且不能實現數組和對象的部分監聽情況;具體也可以看我之前寫的一篇博客: 關於 Vue 不能 watch 數組 和 對象變化的解決方案,最新的 Proxy,相比 vue2 的 Object.defineProperty,能達到速度加倍、內存減半的成效。具體是怎麼實現、以及對比舊的實現方法為啥能有速度加倍、內存減半的特性,下面我們來聊聊:
Vue 初始化過程
Vue 的初始化過程,分別有Observer、Compiler和Watcher,當我們 new Vue 的時候,會調用Observer,通過 Object.defineProperty 遍歷 vue 對象的 data、computed 或者 props(如果是組件的話)的所有屬性進行監聽。同時通過Compiler解析模板指令,解析到屬性後就 new 一個Watcher並綁定更新函數到 watcher 當中,Observer 和 Compiler 就通過屬性來進行關聯。
Object.defineProperty
來個簡單的 Object.defineProperty例子
class Observer { constructor(data) { // 遍歷參數 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("你設置瞭" + key); //你設置瞭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 中的數據規模龐大,那麼遍歷起來就會慢很多.同理,如果 vue 對象的 data/computed/props 中的數據規模龐大,那麼 Object.defineProperty 需要監聽所有的屬性的變化,那麼占用內存就會很大。
Proxy
我們來看看proxy
Proxy 對象用於定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數調用等)
可以理解為在對象之前設置一個”攔截“,當監聽的對象被訪問的時候,都必須經過這層攔截。可以在這攔截中對原對象處理,返回需要的數據格式,也就是無論訪問對象的什麼屬性,之前定義的或是新增的屬性,都會走到攔截中進行處理。這就解決瞭之前所無法監聽的問題。
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("設置的屬性為:" + 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);輸出結果為:設置的屬性為:age 新的屬性:age 值為:22查看的屬性為:age22 設置的屬性為:single 新的屬性:single 值為:NO查看的屬性為:singleNO 查看的屬性為:others 查看的屬性為:others boost
由上可知,新增或編輯屬性,並不需要重新添加響應式處理,都能監聽的到,因為 Proxy 是對對象的操作,隻要你訪問對象,就會走到 Proxy 的邏輯中。Reflect 是一個內置的對象,它提供攔截 JavaScript 操作的方法。這些方法與proxy handlers的方法相同。Reflect不是一個函數對象,因此它是不可構造的,區別Proxy 和 Object.defineProperty 的使用方法看似很相似,其實 Proxy 是在更高維度上去攔截屬性的。
Object.definePropertyVue2 中,對於給定的 data:如 { count: 1 },是需要根據具體的 key 也就是 count,去對 get 和 set 進行攔截,也就是:
Object.defineProperty(data, 'count', { get() {}, set() {},})
必須預先知道要攔截的 key 是什麼,這也就是為什麼 Vue2 裡對於對象上的新增屬性無能為力,所以 Vue 初始化的過程中需要遍歷 data 來挾持數據變化,造成速度變慢,內存變大的原因。
Proxy 而 Vue3 所使用的 Proxy,則是這樣攔截的:
new Proxy(data, { get(key) { }, set(key, value) { },})
可以看到,proxy 不需要關心具體的 key,它去攔截的是修改 data 上的任意 key和讀取 data 上的任意 key
所以,不管是已有的 key 還是新增的 key,都會監聽到。
到此這篇關於Vue3使用Proxy實現數據監聽的原因分析的文章就介紹到這瞭,更多相關Vue3為什麼使用Proxy實現數據監聽內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- vue3結構賦值失去響應式引發的問題思考
- vue3響應式Proxy與Reflect的理解及基本使用實例詳解
- proxy實現vue3數據雙向綁定原理
- vue設計與實現合理的觸發響應
- 詳解Vue3的響應式原理解析