• 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对象里面,但是依旧可以新增。



    不积跬步无以至千里
  • 相关阅读:
    结对(求数组的最大子数组和)
    结对3(求一维数组最大子数组的和扩展)
    结对3(电梯调度需求分析)
    结对开发2(求二维数组的最大子数组和)
    四则运算3(四则运算2程序的扩展)
    四则运算2代码测试
    四则运算2程序及运行
    C++输出四则运算设计题的思路
    C++编程显示四则运算题目
    软件课外读物阅读计划
  • 原文地址:https://www.cnblogs.com/lyt0207/p/12540091.html
Copyright © 2020-2023  润新知