likes
comments
collection

Vue.js 源码分析 - 数据响应式原理-数组

作者站长头像
站长
· 阅读数 9

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战

数据响应式原理-数组

数组的响应式核心代码,在Observer类的构造函数中

  • 路径:Observer:src\core\observer\index.js
if (Array.isArray(value)) {
  if (hasProto) {
    protoAugment(value, arrayMethods)
  } else {
    copyAugment(value, arrayMethods, arrayKeys)
  }
  // 为数组中的每一个对象创建一个 observer 实例
  this.observeArray(value)
}

hasProto

这个属性是干嘛用的呢?

我们先来研究一下代码:

export const hasProto = '__proto__' in {}

这么一看代码很简单了,hasProto他的目的是为了判断当前浏览器是否支持__proto__也就是用来处理兼容性的问题的

arrayMethods

路径:src\core\observer\array.js

const arrayProto = Array.prototype
// 使用数组的原型创建一个新的对象
export const arrayMethods = Object.create(arrayProto)

这里就算是比较复杂了,首先我们要先回忆一下,我们对数组进行操作的方法

  • push
  • pop
  • shift
  • unshift
  • splice
  • sort
  • reverse 是不是就这几个!

然后vue中对这几个方法进行了重写

methodsToPatch.forEach(function (method) {
  // cache original method
  // 保存数组原方法
  const original = arrayProto[method]
  // 调用 Object.defineProperty() 重新定义修改数组的方法
  def(arrayMethods, method, function mutator (...args) {
    // 执行数组的原始方法
    const result = original.apply(this, args)
    // 获取数组对象的 ob 对象
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      // splice的第三个方法就是新增的元素
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 对插入的新元素,重新遍历数组元素设置为响应式数据
    if (inserted) ob.observeArray(inserted)
    // notify change
    // 调用了修改数组的方法,调用数组的ob对象发送通知
    ob.dep.notify()
    return result
  })
})

protoAugment

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

这个方法非常简单,他去重新设置当前数组的原生属性让他等于我们传递来的参数arrayMethods,这个参数中有我们重写过的方法,但是他的原型指向数组的原型

copyAugment

protoAugment方法的前两个参数是一样的,但是多出arrayKeys,这个属性是为了获取我们arrayMethods中重写过的方法

function copyAugment (target: Object, src: Object, keys: Array<string>) {
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i]
    def(target, key, src[key])
  }
}

protoAugment 和 copyAugment的作用是一样的,就是把我们重写完毕的方法指向到数组的原型对象上

observeArray

observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }

遍历数组中的对象,转换成响应式对象

总结

数组的数据响应式原理核心就是把原有的会改变数组数据的方法调用 Object.defineProperty() 重新定义修改数组的方法,当这些方法会被调用的时候,对插入的新元素,重新遍历数组元素设置为响应式数据