【Vue2.x 源码学习】第八篇 - 数组的深层劫持

467 阅读4分钟

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

一,前言

上篇,通过 Vue Demo 的断点调试,对当前版本数据劫持、数据代理进行了简单的流程梳理

同时,对照 Vue2.x 提供的功能,分析了当前版本数据观测的问题和不足

本篇,数组的深层观测


二,抛出问题

let vm = new Vue({
  el: '#app',
  data() {
    return { 
      arr: [
        [123],// 数组中的数组,不会被深层观测
        [456],
        [789],
      ] 
    } 
  }
})

vm.arr[0].push(0)	// 对数组中的数组执行 push 操作

当前版本源码,处理数组类型的数据时,通过重写当前数组的原型方法,从而实现数组的数据劫持;

但是,如果数组中还嵌套着数组,当前版本对于嵌套数组是不会进行深层劫持的;

三,数组深层劫持的思路

目前仅对最外层数组进行了原型方法的重写,想要对数组内嵌套的数组实现数据观测能力,就需要对数组内部的数据继续进行递归处理;


四,数组深层劫持的实现

通过以上分析,实现数组的深层劫持,需要处理两种情况:

  • 数组中嵌套数组(数组套数组)
  • 数组中嵌套对象(数组套对象)

在 Observer 类中,创建 observeArray 方法,对数组进行深层观测:

// src/observe/index.js

class Observer {

  constructor(value) {
    if (isArray(value)) {
      value.__proto__ = arrayMethods;
      this.observeArray(value);	// 对数组数据类型进行深层观测
    } else {
      this.walk(value);
    }
  }

  /**
   * 遍历数组,对数组中的对象进行递归观测
   *  1)[[]] 数组套数组
   *  2)[{}] 数组套对象
   * @param {*} data 
   */
  observeArray(data) {
    console.log("进入 observeArray, 对数组类型进行观测,当前数组: "+ JSON.stringify(data))
    // 对数组中的每一项调用 observe 方法,继续进行深层观测处理;
    // observe 方法内:如果是对象类型,继续 new Observer 进行递归处理
    data.forEach(item => observe(item))  
  }
}

export function observe(value) {

  // 1,经过前面的处理 value 一定是对象,如果此时不是对象说明有问题,直接 return 掉
  // 注意:数组也是对象,但 value 不能是数组;(在 vue 中,data 不能是数组)
  if (!isObject(value)) {
    console.log("observe 方法结束: value不为对象, 当前 value: " + value)
    return;
  }

   // 2,观测 value 对象,实现数据响应式
  return new Observer(value);
}

observeArray 方法:

  • 递归遍历数组,对数组的每一项调用observe方法,继续进行深层观测处理:
  • 如果数组中是对象,则会继续观测;如果数组中时普通值,则不会继续观测;所以,数组中如果的引用类型是响应式的

image.png

这样,数组中的数组 [ [] ]、数组中的对象[ {} ],两种情况就都实现了数据的深层观测;


五,注意事项与性能问题

1,注意事项

对于数组类型,目前仅对重写了部分原型方法,并未对数组的每一项进行数据劫持;

  • 数组中的普通值,不能被观测;(仅重写数组方法,递归中对值类型不再处理)
  • 数组中的引用类型,能够被观测;(observe 实现对象类型深层观测)

举例分析:

1,当vm.arr[0]为普通值时:

vm.arr[0] = 100操作数组索引,由于没对数组索引观测,所以不会触发视图更新;

2,当vm.arr[0]为对象时:

vm.arr[0].a = 1修改对象属性,由于对象已具备深层观测,所以会触发视图更新;

2,性能问题

在 Vue2 中,需要对数组的原型方法进行重写,并且当数组中嵌套着数组时,还需要进行递归重写;因此,在实际应用中当数组的层次过深时,就容易产生性能问题;

Vue2 对数据的处理,涉及到了属性、方法以及数组原型链的重写,所以,会有性能问题;


六,结尾

本篇,介绍了数组的深层观测实现,核心几个点如下:

  • 在之前对数组类型的处理中,仅对当前数组进行了部分原型方法重写操作,当通过变异方法操作数组时,相当于实现了数组的单层数据劫持能力;
  • 通过observeArray方法继续处理数组类型,对数组中每一项调用observe进行递归观测,实现了数组中嵌套结构的劫持:数组嵌套数组、数组嵌套对象;

注意:在 observe方法仅处理了对象类型,所以数组中的值类型是不会被观测的;

目前,数据观测剩余问题:

  • 实现对象中新增对象的观测(深层)
  • 实现对象中新增属性的观测
  • 实现数组中新增对象的观测(深层)- 原型方法重写

下一篇,对象中新增对象的深层观测


维护日志

  • 20230111:重新梳理文章内容,调整了文章的目录结构、排版、内容中的代码高亮,对大量文字描述进行优化并添加截图;