vue3中的雙向綁定 proxy


先來回顧一下 Vue2.x的響應式規則:

對象:會遞歸得去循環vue得每一個屬性,(這也是浪費性能的地方)會給每個屬性增加getter和setter,當屬性發生變化的時候會更新視圖。

數組:重寫了數組的方法,當調用數組方法時會觸發更新,也會對數組中的每一項進行監控。

缺點:對象只監控自帶的屬性,新增的屬性不監控,也就不生效。若是后續需要這個自帶屬性,就要再初始化的時候給它一個undefined值,后續再改這個值

          數組的索引發生變化或者數組的長度發生變化不會觸發實體更新。可以監控引用數組中引用類型值,若是一個普通值並不會監控,例如:[1, 2, {a: 3}] ,只能監控a

 

Proxy消除了之前 Vue2.x 中基於 Object.defineProperty 的實現所存在的這些限制:無法監聽 屬性的添加和刪除數組索引和長度的變更,並可以支持 MapSetWeakMapWeakSet

什么是 Proxy?

MDN 上是這么描述的——Proxy對象用於定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數調用等)。

其實就是在對目標對象的操作之前提供了攔截,可以對外界的操作進行過濾和改寫,修改某些操作的默認行為,這樣我們可以不直接操作對象本身,而是通過操作對象的代理對象來間接來操作對象,達到預期的目的~

看一個例子:

let obj = { name:{name:'hhh'}, arr: ['吃','喝','玩'] } //proxy兼容性差 可以代理13種方法 get set //defineProperty 只對特定 的屬性進行攔截 
 let handler = { get (target,key) { //target就是obj key就是要取obj里面的哪個屬性
        console.log('收集依賴') return target[key] }, set (target,key,value) { console.log('觸發更新') target[key] = value } } let proxy = new Proxy(obj,handler) //通過代理后的對象取值和設置值
proxy.arr proxy.name = '123'

 

 

定義了一個對象obj,通過代理后的對象(上面的proxy)來操作原對象。當取值的時候會走get方法,返回對應的值,當設置值的時候會走set方法,觸發更新。

但這是老的寫法,新的寫法是使用Reflect。

Reflect是內置對象,為操作對象而提供的新API,將Object對象的屬於語言內部的方法放到Reflect對象上,即從Reflect對象上拿Object對象內部方法。 如果出錯將返回false

簡單改寫上面這個例子

let handler = { get (target,key) { //target就是obj key就是要取obj里面的哪個屬性
        console.log('收集依賴') // return target[key]
        //Reflect 反射 這個方法里面包含了很多api
        return Reflect.get(target,key) }, set (target,key,value) { console.log('觸發更新') // target[key] = value //這種寫法設置時如果不成功也不會報錯 比如這個對象默認不可配置
 Reflect.set(target,key,value) } } let proxy = new Proxy(obj,handler) //通過代理后的對象取值和設置值
proxy.arr proxy.name = '123'

效果依舊和上面一樣。

但是有一個問題,這個對象是多層對象,它並不會取到里面的那個name的值。

這是因為之前Object.defineProperty方法是一開始就會對這個多層對象進行遞歸處理,所以可以拿到,而Proxy不會。它是懶代理。如果對這個對象里面的值進行代理就取不到值。就像上面我們只對name進行了代理,但並沒有對name.name進行代理,所以他就取不到這個值,需要代理之后才能取到。

let obj = { name:{name:'hhh'}, arr: ['吃','喝','玩'] } //proxy兼容性差 可以代理13種方法 get set //defineProperty 只對特定 的屬性進行攔截 
 let handler = { get (target,key) { //target就是obj key就是要取obj里面的哪個屬性
        console.log('收集依賴') if(typeof target[key] === 'object' && target[key] !== null){ //遞歸代理,只有取到對應值的時候才會代理
            return new Proxy(target[key],handler) } // return target[key]
        //Reflect 反射 這個方法里面包含了很多api
        return Reflect.get(target,key) }, set (target,key,value) { console.log('觸發更新') // target[key] = value //這種寫法設置時如果不成功也不會報錯 比如這個對象默認不可配置
 Reflect.set(target,key,value) } } let proxy = new Proxy(obj,handler)

可以看到這次雖然我寫了代理name.name,但是沒有取到name.name,它並不會真正的代理

 

 這次觸發了兩次收集依賴。拿到了里面name的值。。

接下來看看數組的代理過程:

let obj = { name:{name:'hhh'}, arr: ['吃','喝','玩'] } //proxy兼容性差 可以代理13種方法 get set //defineProperty 只對特定 的屬性進行攔截 
 let handler = { get (target,key) { //target就是obj key就是要取obj里面的哪個屬性
        console.log('收集依賴') if(typeof target[key] === 'object' && target[key] !== null){ //遞歸代理,只有取到對應值的時候才會代理
            return new Proxy(target[key],handler) } // return target[key]
        //Reflect 反射 這個方法里面包含了很多api
        return Reflect.get(target,key) }, set (target,key,value) { console.log('觸發更新') // target[key] = value //這種寫法設置時如果不成功也不會報錯 比如這個對象默認不可配置
        return Reflect.set(target,key,value) } } let proxy = new Proxy(obj,handler) //通過代理后的對象取值和設置值 // proxy.name.name = '123' //設置值,取一次,設置一次
proxy.arr.push(456)

這里面它會走兩次觸發更新的操作,因為第一次需要修改數組的長度,第二次再把元素放進數組里。所以我們需要判斷一下它是新增操作還是修改操作

set (target,key,value) { let oldValue = target[key] console.log(key, oldValue, value) if(!oldValue){ console.log('新增屬性') }else if(oldValue !== value){ console.log('修改屬性') } // target[key] = value //這種寫法設置時如果不成功也不會報錯 比如這個對象默認不可配置
        return Reflect.set(target,key,value) }

首先拿到它的舊值,如果這個值不存在就是新增,如果存在但不相等就是修改操作

 

 可以看到最后一次判斷結果是兩個相等,什么也不做

 

 這是一次修改操作。

我們知道,vue2.0是不會監控到之前不存在的屬性的,但是proxy可以操作之前不存在的屬性的,它會攔截設置操作,如下:

 

 

 xxx之前並不在obj對象里面,但是依舊可以新增。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM