Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vue 是如何对数组方法进行变异的? #70

Open
susouth opened this issue Dec 9, 2019 · 0 comments
Open

vue 是如何对数组方法进行变异的? #70

susouth opened this issue Dec 9, 2019 · 0 comments
Labels
No.73 Vuejs Vuejs构建用户界面的渐进式框架,高频面试题目汇总

Comments

@susouth
Copy link
Contributor

susouth commented Dec 9, 2019

📚在线阅读:vue 是如何对数组方法进行变异的? - No.73

vue 是如何对数组方法进行变异的?例如 push、pop、splice 等方法

解题1:

  1. 为什么要对数组进行单独处理
    在Vue现有阶段中,对响应式处理利用的是Object.defineProperty对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以我们需要对这些操作进行hack,让vue能监听到其中的变化。

2.怎么对数组进行处理

methodsToPatch.forEach(function(method) {
    // cache original method
    // 获取原方法
    var original = arrayProto[method];
    // def方法重新定义arrayMethods的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':
          // [].push(1),[].unshift(1)
          // arg = [1]
          inserted = args;
          break
        case 'splice':
          // [1,2,3].splice(0,1,1)
          // 第三个参数为插入的值
          inserted = args.slice(2);
          break
      }
      if (inserted) { ob.observeArray(inserted); }
      // 监听变化,如果不是插入操作直接循环响应
      // 如果是去除数组参数方法,触发一次notify将会重新计算
      // 如果仅仅是数字数据,任何操作只需要再次执行一次notify则可以
      // 但是如果新增的是一个对象类型,就需要重新监听
      // 为什么用角标和length属性不能监听的原因是因为无法触发obj的get方法,所以没法动态监听
      // notify change
      ob.dep.notify();
      return result
    });
  });

正如该题所问,vue对push,pop,splice等方法进行了hack,hack方式很简单,如果加入新对象,对新对象进行响应式化,至于如何响应式化请参考vue源码。
举例来说对于push和unshift会推入一个新的对象到数组里(不管从前还是从后),记录这个加入的对象,并调用Observe方法将加入的对象转换成响应式对象,对于splice方法,如果加入了新对象也是将该对象响应式化。
最后一步是向外抛出数组变化,提醒观察者进行更新。

3.存在问题
对于Object.defineProperty的缺陷导致如果直接改变数组下标是无法hack的,由于此点,vue提供了$set方法,最新的解决方案当然是利用Proxy对象进行监听,但是Proxy的缺陷在于兼容性,可能会为了性能以及便利而放弃兼容性吧,一切都要看尤大的决定了。

4.变异的本质就在这些方法内部加上自定义的逻辑,其实就是想监听这些方法的调用。

Vue中默认的做法就是在数组实例与它的原型之间,插入了一个新的原型对象,这个原型方法实现了这些变异方法,也就拦截了真正数组原型上的方法(因为原型链的机制,找到了就不会继续往上找了)。 变异方法中增加了自定义逻辑,也调用了真正数组原型上的方法,即实现了目的,也不会对正常使用造成影响。

如果浏览器不支持___proto__这个属性,Vue则直接在数组实例上增加这些变异方法。

解题2 ?:

👇~~~~ 欢迎在下方评论补充你的答案,一起来学习~:pushpin:

扩展阅读:

@susouth susouth added Vuejs Vuejs构建用户界面的渐进式框架,高频面试题目汇总 No.73 labels Dec 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
No.73 Vuejs Vuejs构建用户界面的渐进式框架,高频面试题目汇总
Projects
None yet
Development

No branches or pull requests

1 participant