Vue3Proxy代理

129 阅读3分钟

开始vue3响应式Proxy之前,先回顾一下vue2的响应式原理

vue2响应式

  • 实现原理:
    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
    • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
        get () {}, // 读取
        set () {} // 设置
    })
  • 模拟vue2实现响应式
const person = {
        name:'张三',
        age:18
}
let p = {}
Object.keys(person).forEach(key=>{
        Object.defineProperty(p,key,{
                configurable:true,
                get(){ // 读取时触发
                        return person[key]
                },
                set(value){ //修改时触发
                        console.log('有人修改了person,我发现了,我要去更新界面!');
                        return person[key] = value
                }
        })
})

1683699347662.jpg

image.png

新增时不会触发set()方法,虽然添加了,但是不是响应式数据,从图片中可以看出 age和name是拥有get()set()方法的,拥有get()set()方法才是响应式数据

image.png

删除属性时从图片中可以看出没有删除成功,delete 删除属性时会返回一个Boolean告诉我们是否删除成功,在Object.defineProperties中有一个属性configurable 设置为true,就可以删除了

  • 存在问题:
    • 新增属性、删除属性, 界面不会更新。
    • 直接通过下标修改数组, 界面不会自动更新。
  • 解决方法:
    • this.$set(源数据,属性,值)
    • this.$delete(源数据,属性)
    • Array.splice()

vue3响应式Proxy

new Proxy(data, {
    // 拦截读取属性值
      get (target, prop) {
            return Reflect.get(target, prop)
      },
      // 拦截设置属性值或添加新属性
      set (target, prop, value) {
            return Reflect.set(target, prop, value)
      },
      // 拦截删除属性
      deleteProperty (target, prop) {
            return Reflect.deleteProperty(target, prop)
      }
  })
  • Reflect(反射) 通过Reflect读取对象属性 Reflect.get(obj,xxx) 修改对象属性 Reflect.set(obj,'xxx',xxx) 删除对象属性 Reflect.deleteProperty(obj,xxx)
Object.defineProperty(obj,'c',{
    get(){
            return 3
    }
            })
Object.defineProperty(obj,'c',{
    get(){
            return 4
    }
})
// 直接报错 cannot redefine property:c  不往下执行

const r1 = Reflect.defineProperty(obj,'c',{
    get(){
            return 3
    }
})
const r1 = Reflect.defineProperty(obj,'c',{
    get(){
            return 4
    }
})
console.log('继续执行了',r1,r2); //r1-->true r2 -->false

Reflect 在框架封装中可以减少try catch的使用 更友好一点

  • 模拟vue3响应式
const person = {
    name:'张三',
    age:18
}
//   Proxy 第一个参数是源数据,第一个参数为必填,可以为空对象
    let p = Proxy(person,{}) // 这样可以实现一个代理
    const person = {
        name:'张三',
        age:18
    }
    //   Proxy 第一个参数是源数据,第一个参数为必填,可以为空对象 为空对象时可以实现代理,但是代理不是响应式
    //   let p = Proxy(person,{})
    let p = Proxy(person,{
    // get 有两个参数 target和propName target-->源数据  propName-->读取的属性名
        get(target,propName){
            console.log(`有人读取了p身上的${propName}属性`);
            return Reflect.get(target, propName)
        },
        //  set 有三个参数 target和propName target-->源数据  propName-->读取的属性名 value-->修改的值 
        //  proxy的set方法可以监听到修改和新增
        set(target,propName,value){
            console.log(`有人修改了p身上的${propName}属性,我要去更新页面了!`);
            return Reflect.set(target, propName, value)
        },
        // deleteProperty 有两个参数 target和propName target-->源数据  propName-->读取的属性名
        deleteProperty(target,propName){
            console.log(`有人删除了p身上的${propName}属性,我要去更新页面了!`);
             return Reflect.deleteProperty(target, propName)
        }
   })

完结散花...