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