vue对于常用数组方法重写的原因和源码的学习(vue面试必会知识点)

2,461 阅读1分钟

由于Object.defineproperty方法无法监听到数组和对象内的新增变化,所以vue2重写了常用的数组方法来使数组更新时能触发页面更新,对于对象的更新则采用this.$set的方法,使得对象更新时同时触发视图更新,这个之后再谈,今天我们来看一下vue内部是怎么重写常用的数组方法的。 1.vue2重写了哪些数组方法? 如下图,一共7中常用数组方法(来自源码)

var methodsToPatch = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
  ];

也就意味着,如果使用这7种以外的数组方法更新数组,且需要视图也更新,则需要使用this.$set。 2.怎么重写数组方法 在重写之前先进行了以下两个步骤:

  var arrayProto = Array.prototype;
  var arrayMethods = Object.create(arrayProto);

arrayProto用来存储原生js的数组方法,用于接下来的遍历,arrayMethods定义一个空对象用来存放vue重写的数组方法以避免污染Array.prototype上的数组方法。 接下来是重写数组方法:

  /**
   * Intercept mutating methods and emit events
   */
  methodsToPatch.forEach(function (method) {
    // cache original method
    var original = arrayProto[method];
    def(arrayMethods, method, function mutator () {
      var args = [], len = arguments.length;
      while ( len-- ) args[ len ] = arguments[ len ];

      var result = original.apply(this, args);
      var ob = this.__ob__;
      var 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
    });
  });

由上述代码可见,由于push、unshift、splice会让数组索引发生改变,所以需要手动触发observer,这里的方法是定义一个inserted来控制是否触发响应式更新,如果为true,则用ob.observeArray(inserted);来手动给新插入的值设置响应式监听,再用ob.dep.notify()通知依赖更新,最后返回原生数组方法处理后的值 return result

3.相关知识点:

3.1 def方法源码,在数组方法的重写中,用到了该方法来定义vue自己的一些数组方法,便于vue对一些需要重写的数组方法进行数据劫持。

  function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
      value: val,
      enumerable: !!enumerable,
      writable: true,
      configurable: true
    });
  }

3.2 observeArray方法的源码,在数组方法重写中,用于将新增的数组元组变成响应式的。

Observer.prototype.observeArray = function observeArray (items) {
    for (var i = 0, l = items.length; i < l; i++) {
      observe(items[i]);
    }
  };