vue3中的雙向綁定 proxy


什么是 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