Array的变化侦测

4rfEwQ.jpg

为什么数组和对象的侦测方式不同?

可能很多人不理解为什么Array的侦测方式和Object的不同,举例说明:

this.list.push(1)

如上,我们是使用push方法向list中新增了数字1。之前介绍Object的时候,其侦测方式是通过getter/setter实现的,但是数组是通过使用push方法来改变数组,就不能触发getter/setter。

如何追踪数组变化

Object是的变化是靠追踪setter,只要数据发送变化,就会触发setter。
同理,数组通过方法来更改,只要在用户使用方法的操作数组的时候得到通知,就能实现同样的目的。

这样通过拦截器,我们就可以追踪到Array的变化。

拦截器

拦截器就是一个和Array.prototype一样的Object,里面包含的属性一模一样,只不过这个Object中的方法是我们处理过的。
那我们来处理一下:

    const arrayProto = Array.prototype
    export const arrayMethods = Object.create(arrayProto)
    ['push','pop','shift','unshift','splice','sort','reverse'].forEach(method => {
      //缓存原始方法
      const original = arrayProto[method]
      Object.defineProperty(arrayMethods, method, {
        value: function mutator(...args) {
          return original.apply(this, args)
        },
        enumerable: false,
        writable: true,
        configurable: true
      })
    });

在上面代码中,我们将所有的数组方法通过Object.prototype进行封装,所以当使用push方法的时候,其实使用的是arrayMethods.push,而arrayMethods.push是mutator函数,在mutator函数中执行original来做它该做的事,例如push。所以我们就可以在mutator中做一些其他的事,比如发送通知

使用拦截器覆盖Array原型

有了拦截器,就要让他生效,所以需要去覆盖Array.prototype。但是不能直接覆盖,直接覆盖会污染全局的Array。我们只希望拦截操作只针对那些被侦测了变化的数组,也就是只覆盖这一部分的响应式数组的原型。
而将一个数据转换为响应式,需要通过Observer(vue响应式原理有讲,https://www.cnblogs.com/taosifan/p/15329016.html),所以只需要在Observer中使用拦截器覆盖这些数组的原型就好了:

    export class Observer {
      constructor (value) {
        this.value = value
        if(!Array.isArray(value)) { //判断是不是数组,数组需要单独进行特殊处理
          this.walk(value)
        } else {
          value.__proto__ = arrayMethods //覆盖原型
        }
      }

好了,现在的结构就变成了这样:

将拦截器直接挂载到数组的属性上

对于有些不支持__proto__的情况,我们可以将arrayMethods身上的方法设置到被侦测的数组上:

export class Observer {
  constructor(value) {
    this.value = value
    if(!Array.isArray(value)) {
      this.walk(value)
    } else {
      const augment = hasProto ? protoAugment : copyAugment
      augment(value, arrayMethods, arrayKeys)
    }
  }
}

function protoAugment(target, src, keys) {
  target.__proto__ = src
}

function copyAugment(target, src, keys) {
  for (let i in keys) {
    const key = keys[i]
    def(target, key, src[key])
  }
}

因为当访问一个对象的方法时,只有其自身没有这个方法,才会去原型上找这个方法

如何收集Array的依赖

后面好多啊,我想鸽一下。结构先搭起

如何去通知watcher

如何监听添加或者删除

关于Array的问题

4rHBo8.png

posted @ 2021-09-25 13:10  卿六  阅读(71)  评论(0编辑  收藏  举报