第43题 说一下vue2.x中如何监测数组变化

使用了函数劫持的方式,重写了数组的方法,Vuedata中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。

    // 源码实现
    /*
     * not type checking this file because flow doesn't play well with
     * dynamically accessing methods on Array prototype
     */
    
    /**
     * Define a property.
     */
    export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
      Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true
      })
    }
    
    const arrayProto = Array.prototype 
    const arrayMethods = Object.create(arrayProto)
    
    /**
     * Intercept mutating methods and emit events
     */
    ;[
      'push',
      'pop',
      'shift',
      'unshift',
      'splice',
      'sort',
      'reverse'
    ]
    .forEach(function (method) {
      // cache original method
      const original = arrayProto[method]
      def(arrayMethods, method, function mutator (...args) {
        const result = original.apply(this, args)
        const ob = this.__ob__
        let inserted
        switch (method) {
          case 'push':
          case 'unshift':
            inserted = args
            break
          case 'splice':
            inserted = args.slice(2)
            break
        }
        if (inserted) ob.observeArray(inserted)
        // notify change
        ob.dep.notify()
        return result
      })
    })

补充(现代做法):以上是 Vue2 的做法——因为 Object.defineProperty 无法侦测数组「按下标赋值」和「修改 length」,Vue2 只能重写 7 个变异方法(push/pop/shift/unshift/splice/sort/reverse)来手动触发更新,所以 this.arr[0]=xthis.arr.length=0 不会响应,要用 Vue.set/splice。Vue3 改用 Proxy 整体代理对象/数组,原生就能拦截下标赋值、新增属性、deletelength 变化,不再需要重写数组方法,也不再有 Vue.set 的限制。

Last Updated:
Contributors: leeguooooo