天天看点

vue如何对数组做响应式的

你在面试中,有没有被问到vue是如何对数组做响应式处理的呢?

1、找到响应式处理入口

进入页面,Vue开始初始化,执行new Vue(),进入到Vue的构造函数中,在构造函数中执行了

_init()

方法。

_init()

这个方法中,调用了initState()方法 => 在initState()方法中,调用了initData()方法 => 在initData()方法中,调用了observe()方法。

observe()方法就是Vue进行响应式处理的入口。

2、找到数组响应式处理入口

在observe()方法中,创建了Observer实例,在创建Observer实例的过程中,对传入的数据进行了判断,如果是数组,单独对数组进行处理,如果不是数组,调用walk方法对数据进行响应式处理。本文只关心对数组的处理,源码如下(已精简)

constructor (value: any) {
	// 如果是数组,对数组进行特殊处理
    if (Array.isArray(value)) {
      // 判断浏览器是否支持原型 __proto__ 下面两个方法实质处理方式是一样的
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      // 遍历数组的每一项,调用observe方法对数组的每一项以递归的方式进行响应式处理
      this.observeArray(value)
    } else {
      this.walk(value)
    }
 }
           

3、对数组进行处理

这里假设浏览器支持

__proto__

原型,进入到protoAugment方法中,这个方法很简单,

target.__proto__ = src

就是将数组的原型指向arrayMethods。我们主要关心的就是arrayMethods,这也是对数组处理的核心部分。其实这一部分也很简单,直接贴上源码(已精简):

const arrayProto = Array.prototype
// arrayMethods 的原型就是数组的原型
export const arrayMethods = Object.create(arrayProto)
// 数组中会修改自身的7个方法
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse']
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  // 通过def方法将数组中会修改自身的方法进行重写
  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 数组改变后调用 notify方法 通知 Watcher 去更新视图
    ob.dep.notify()
    return result
  })
})
           

从源码上很容易可以看出,vue对数组的响应式处理,其实质还是调用了数组原型上的方法,在数组发生变化后调用了notify去派发更新。

4、几个需要注意的点

1> ES6新增的fill方法、copyWithin方法也会改变数组自身,不会触发视图更新

2> 使用数组索引的方式修改数据,不会触发视图更新

3> 修改数组的length属性,不会触发视图更新

5、延伸

这里提到了数组发生变化后,调用了notify方法通知Watcher更新视图。那么面试官可能会追问你,什么时候进行的依赖收集呢?你可以看一下这篇文章:vue依赖收集的具体时机