Vue3響應式對象是如何實現的?
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
概述 Vue3的發布已過去3年時間,其更小的體積和更快的渲染機制給開發帶來了更好的體驗,它向下兼容 Vue2.x 版本,優化了主要核心雙向綁定原理和體積大小,并且更加友好的兼容ts語法。在性能方面相對于Vue2做了以下改進: ①重寫虛擬DOM的實現,并引入Tree-Shaking將打包體積減少41%。 ②引入可以按需使用的Composition API,多余勾子配置不用再次打包。 ③初次渲染快55%,更新渲染快133%,內存減少54%。 ④使用Proxy代替defineProperty實現響應式。 ⑤在源碼方面,移除了一些冷門API,比如filter、inline-template。 本文將在對Vue3進行簡介的基礎上比較Vue2與Vue3響應式特性的不同,并著重介紹Vue3響應式對象的實現原理。 Vue2與Vue3的 響應式對象實現 在Vue2中對象響應式基于Object對象上的defineProperty()實現,通過defineProperty方法對對象的已有屬性值的讀取和修改進行攔截,通過重寫數組更新數組一系列更新元素的方法來實現元素修改的攔截。例如: Object.defineProperty(data, 'count', { get () {}, set () {} }) 使用Object對象上的靜態方法defineProperty為data的每一個屬性值設置存取器函數,就能在屬性讀取與修改時作出相應的反應。 但這樣的實現方式存在一些不足: ①在目標對象上新增和刪除屬性時無法進行攔截,因此界面不會響應式地更新。 ②通過下標修改數組元素或是修改數組的length屬性,也無法進行攔截和響應。 對此,Vue3針對對象響應式進行了新的實現。 Vue3響應式的核心原理是對需要進行響應式處理的對象整體進行代理操作,而不再是對對象已有屬性進行存取器設置。具體來講,是通過Proxy(代理)和Reflect(反射)實現:通過Proxy攔截對data任意屬性的任意(13種)操作,包括屬性值的讀寫、屬性的添加、屬性的刪除等;并通過 Reflect動態地對被代理對象的相應屬性進行相應操作。 ECMAscript6新增的代理和反射為開發者提供了攔截并向基本操作嵌入額外行為的能力,即可以給目標對象定義一個關聯的代理對象,而這個代理對象可以作為抽象的目標對象來使用。在對目標對象的各種操作施加影響之前,可以在代理對象中對這些操作加以控制。 Proxy代理器 創建代理 Proxy代理是ES6中新增的基礎性語言能力,在ES6之前,ECMAscript中并沒有類似代理的特性。在代理對象上執行的所有操作都會傳播到目標對象,在任何可以使用目標對象的地方,都可以通過同樣的方式來使用與之關聯的代理對象。 代理是使用Proxy構造函數創建的,它接收兩個參數:目標對象和處理程序對象,要創建空代理可以傳一個簡單的對象字面量作為處理程序對象,但不可以缺省處理程序對象。如下所示,在代理對象上執行的任何操作實際上都會應用到目標對象:
定義捕獲器 代理的主要目的是可以在處理程序對象中定義捕獲器。每個處理程序對象可以包含零個或多個捕獲器,每個捕獲器都對應一種基本操作,每次在代理對象上調用這些基本操作時,代理可以在這些操作傳播到目標對象之前先調用捕獲器函數,從而攔截并修改相應的行為。 例如,可以定義一個get()捕獲器,在ECMAscript操作以某種形式調用get時觸發。
當通過代理對象執行get()操作時,就會觸發定義的get()捕獲器,只有在代理對象上執行相應操作才會觸發捕獲器,在目標對象上執行這些操作仍然會產生正常的行為。 反射API 所有捕獲器都可以訪問相應的參數,基于這些參數可以重建被捕獲方法的原始行為,但開發者并不需要手動費時費力重建原始行為,而是可以通過調用全局Reflect對象上的同名方法來輕松重建。
通過Proxy代理對象可以攔截對目標對象的13種操作,并通過Reflect對象上相應的同名方法重建操作。在此基礎上開發者可以用最少的代碼修改捕獲的方法,實現對應的業務邏輯。 例如:通過捕獲get、set和has等操作,可以知道屬性什么時候被訪問、被查詢。把實現相應捕獲器的某個對象代理放到應用中,可以監控這個對象何時在何處被訪問過。 const company = { name: 'ebChinaTech' } const handler = { get(target, property, receiver){ console.log(`Getting ${property}`) return Reflect.get(...arguments) }, set(target, property, value, receiver){ console.log(`Setting ${property} = ${value}`) return Reflect.set(...arguments) } } const proxy = new Proxy(company , handler) proxy.name //Getting name proxy.age = 3 //Setting age = 3 Proxy代理對象還可用于在前端對用戶隱藏某些對象屬性,例如: const hiddenProperties = ['address', 'manager'] const company = { name: 'ebChinaTech', address: 'Beijing', manager: 'Li' } const handler = { get(target, property){ if (hiddenProperties.includes(property)){ return undefined } else { return Reflect.get(...arguments) } }, has(target, property){ if (hiddenProperties.includes(property)){ return false } else { return Reflect.has(...arguments) } } } const proxy = new Proxy(company, handler) console.log(proxy.address) //undefined console.log(proxy.manager) //undefined console.log(proxy.name) //ebChinaTech console.log('address' in proxy) //false console.log('manager' in proxy) //false console.log('name' in proxy) //true 代理的應用場景是不可限量的,開發者使用它可以創建出各種編碼模式,比如跟蹤屬性訪問、隱藏屬性、阻止修改或刪除屬性、函數參數驗證、構造函數參數驗證、數據綁定以及可觀察對象。由于proxy的性能比defineproperty好,因此vue3通過使用proxy重寫對象響應式的同時,也提升了vue架構中高頻使用的數據特性的性能,大大提升了開發體驗。而vue3的其他方面優化,例如源碼體積的優化以及打包工具的優化,則為部署提供了更好的解決方案,也成為超越vue2的前端框架。 文章作者:潘嘉偉 手繪插畫:Lina 該文章在 2023/11/20 12:53:10 編輯過 |
關鍵字查詢
相關文章
正在查詢... |