当前位置 主页 > 网站技术 > 代码类 >

    为什么Vue3.0使用Proxy实现数据监听(defineProperty表示不背这个(3)

    栏目:代码类 时间:2019-11-08 15:02

    三 Object.defineProperty VS Proxy

    上面已经知道 Object.defineProperty 对数组和对象的表现是一致的,那么它和 Proxy 对比存在哪些优缺点呢?

    1. Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象。

    由于 Object.defineProperty 只能对属性进行劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。

    2. Object.defineProperty对新增属性需要手动进行Observe。

    由于 Object.defineProperty 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性再使用 Object.defineProperty 进行劫持。

    也正是因为这个原因,使用vue给 data 中的数组或对象新增属性时,需要使用 vm.$set 才能保证新增的属性也是响应式的。

    下面看一下vue的 set 方法是如何实现的, set 方法定义在 core/observer/index.js ,下面是核心代码。

    /**
     * Set a property on an object. Adds the new property and
     * triggers change notification if the property doesn't
     * already exist.
     */
    export function set (target: Array<any> | Object, key: any, val: any): any {
     // 如果target是数组,且key是有效的数组索引,会调用数组的splice方法,
     // 我们上面说过,数组的splice方法会被重写,重写的方法中会手动Observe
     // 所以vue的set方法,对于数组,就是直接调用重写splice方法
     if (Array.isArray(target) && isValidArrayIndex(key)) {
     target.length = Math.max(target.length, key)
     target.splice(key, 1, val)
     return val
     }
     // 对于对象,如果key本来就是对象中的属性,直接修改值就可以触发更新
     if (key in target && !(key in Object.prototype)) {
     target[key] = val
     return val
     }
     // vue的响应式对象中都会添加了__ob__属性,所以可以根据是否有__ob__属性判断是否为响应式对象
     const ob = (target: any).__ob__
     // 如果不是响应式对象,直接赋值
     if (!ob) {
     target[key] = val
     return val
     }
     // 调用defineReactive给数据添加了 getter 和 setter,
     // 所以vue的set方法,对于响应式的对象,就会调用defineReactive重新定义响应式对象,defineReactive 函数
     defineReactive(ob.value, key, val)
     ob.dep.notify()
     return val
    }

    set 方法中,对 target 是数组和对象做了分别的处理, target 是数组时,会调用重写过的 splice 方法进行手动 Observe

    对于对象,如果 key 本来就是对象的属性,则直接修改值触发更新,否则调用 defineReactive 方法重新定义响应式对象。

    如果采用 proxy 实现, Proxy 通过 set(target, propKey, value, receiver) 拦截对象属性的设置,是可以拦截到对象的新增属性的。

    不止如此, Proxy 对数组的方法也可以监测到,不需要像上面vue2.x源码中那样进行 hack

    完美!!!

    3. Proxy支持13种拦截操作,这是defineProperty所不具有的

    get(target, propKey, receiver):拦截对象属性的读取,比如