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

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

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

    只有索引为0,1,2的属性才会触发 getter

    这里我们可以对比对象来看,arr数组初始值为[1, 2, 3],即只对索引为0,1,2执行了 observe 方法,所以无论后来数组的长度发生怎样的变化,依然只有索引为0,1,2的元素发生变化才会触发,其他的新增索引,就相当于对象中新增的属性,需要再手动 observe 才可以。

    4. 数组的 pop 方法

    当移除的元素为引用为2的元素时,会触发 getter

    删除了索引为2的元素后,再去修改或获取它的值时,不会再触发 settergetter

    这和对象的处理是同样的,数组的索引被删除后,就相当于对象的属性被删除一样,不会再去触发 observe

    到这里,我们可以简单的总结一下结论。

    Object.defineProperty 在数组中的表现和在对象中的表现是一致的,数组的索引就可以看做是对象中的 key

    通过索引访问或设置对应元素的值时,可以触发 gettersetter 方法 通过 pushunshift 会增加索引,对于新增加的属性,需要再手动初始化才能被 observe 。 通过 popshift 删除元素,会删除并更新索引,也会触发 settergetter 方法。

    所以, Object.defineProperty 是有监控数组下标变化的能力的,只是vue2.x放弃了这个特性。

    二、vue对数组的observe做了哪些处理?

    vue的 Observer 类定义在 core/observer/index.js 中。

    可以看到,vue的 Observer 对数组做了单独的处理。

    hasProto 是判断数组的实例是否有 __proto__ 属性,如果有 __proto__ 属性就会执行 protoAugment 方法,将 arrayMethods 重写到原型上。 hasProto 定义如下。

    arrayMethods 是对数组的方法进行重写,定义在 core/observer/array.js 中, 下面是这部分源码的分析。

    /*
     * not type checking this file because flow doesn't play well with
     * dynamically accessing methods on Array prototype
     */
    
    import { def } from '../util/index'
    
    // 复制数组构造函数的原型,Array.prototype也是一个数组。
    const arrayProto = Array.prototype
    // 创建对象,对象的__proto__指向arrayProto,所以arrayMethods的__proto__包含数组的所有方法。
    export const arrayMethods = Object.create(arrayProto)
    
    // 下面的数组是要进行重写的方法
    const methodsToPatch = [
     'push',
     'pop',
     'shift',
     'unshift',
     'splice',
     'sort',
     'reverse'
    ]
    
    /**
     * Intercept mutating methods and emit events
     */
    // 遍历methodsToPatch数组,对其中的方法进行重写
    methodsToPatch.forEach(function (method) {
     // cache original method
     const original = arrayProto[method]
     // def方法定义在lang.js文件中,是通过object.defineProperty对属性进行重新定义。
     // 即在arrayMethods中找到我们要重写的方法,对其进行重新定义
     def(arrayMethods, method, function mutator (...args) {
     const result = original.apply(this, args)
     const ob = this.__ob__
     let inserted
     switch (method) {
     // 上面已经分析过,对于push,unshift会新增索引,所以需要手动observe
     case 'push':
     case 'unshift':
     inserted = args
     break
     // splice方法,如果传入了第三个参数,也会有新增索引,所以也需要手动observe
     case 'splice':
     inserted = args.slice(2)
     break
     }
     // push,unshift,splice三个方法触发后,在这里手动observe,其他方法的变更会在当前的索引上进行更新,所以不需要再执行ob.observeArray
     if (inserted) ob.observeArray(inserted)
     // notify change
     ob.dep.notify()
     return result
     })
    })