vue源码解析 002 双向数据绑定时 数据类型为数组的处理

为什么要重写数组方法触发视图更新

如果数据类型是对象的时候,可以使用object.defineProperty实现对数据的监监听

但是当数据类型是数组的时候,如果有成千上万条数据呢,每个数据都进行监听,就太消耗性能了,所有我们重写了改变原数组的方法,在调用这些方法时触发更新

数据监听的index.js文件

import { arrayMethods } from "./array";

class Observer {
  constructor(value) {

    console.log("value10000",value)
    //需要对这个value属性重新定义
    //可能是对象,可能是数组
    // value.__ob__ = this//给每个属性
    Object.defineProperty(value, "__ob__", {
      value: this,
      enumerable: false, //表示不能枚举
      configurable: false, //表示不能删除
    });
    if (Array.isArray(value)) {
      console.log("55555555555555")
      //数组不用defineProperty来进行代理,性能不好
      // push,shift,unshift,reverse,sort 将这方法重写,增加更新逻辑
      value.__proto__ = arrayMethods; //当是数组时,改写方法为自己重写后的方法

      this.observeArray(value); //原有数组中的对象  Object.freeze()
    } else {
      console.log("6666666666666666")
      this.walk(value);
    }
  }
  observeArray(value) {
    console.log("0000",value)
    for (let i = 0; i < value.length; i++) {
      const ele = value[i];
      observe(ele);
    }
  }
  walk(data) {
    // 将对象中的所有key,重新用defineProperty 定义成响应式的

    // 数组处理 如果数组也拦截 会十分浪费性能
    Object.keys(data).forEach((key) => {
      defineReactive(data, key, data[key]);
    });
  }
}

export function defineReactive(data, key, value) {
  // value 可能也是一个对象
  /**
   * vue2中数据嵌套不要过深  过深浪费性能
   *
   *
   *
   *
   */
  observe(value); //对结果递归拦截
  Object.defineProperty(data, key, {
    get() {
      return value;
    },
    set(newValue) {
      if (newValue === value) {
        return;
      }
      observe(newValue); //如果用户设置的是一个对象,就继续将用户设置的对象变成响应式的
      value = newValue;
    },
  });
}

export function observe(data) {
  console.log("observedata",data)
  // 只对对象类型进行观测  非对象无法关测
  if (typeof data !== "object" || data == null) {
    return;
  }
  if (data.__ob__) {
    // 对象已经观测过了,防止循环引用
    return;
  }
  // 通过类来实现对对象的观测,类可以方便扩展,会产生实例
  console.log("observedata1111",data)
  return new Observer(data);
}

注解

  1. 当数据类型时object 类型并且 对象没有被观测过防止循环引用
  2. 需要判断数据是数组还是对象
    1. 如果是对象,就使用object.defineProperty将对象的key定义为响应式的
    2. 如果是数组,不能挂测所有的数据,因为数组数据太多消耗性能,所以我们选择重写数通过这些重写的方法触发视图更

数组处理的代码

//先要拿到数组原型上的方法
let oldArrayProtoMethods = Array.prototype;
// 不能直接改写数组方法,只有被vue控制的数组才需要改写

export let arrayMethods = Object.create(Array.prototype);

let methods = ["push", "pop", "shift", "unshift", "splice", "reverse", "sort"];
// 改边原数组
// concat slice 都不能改变原数组
methods.forEach((method) => {
  //AOP  切片编程
  arrayMethods[method] = function (...args) {
    //重写数组方法
    //更新视图
    //数组变化
    // todo····
    // 有可能用户新增的数据是对象格式也需要进行拦截

    let result = oldArrayProtoMethods[method].call(this, ...args);
    let inserted;
    switch (method) {
      case "push":
        inserted = args;
        break;
      case "unshift":
        inserted = args;
        break;
      case "splice":
        inserted = args.slice(2); //第三个参数才是需要新增的数据
      default:
        break;
    }
    console.log("数据更新");
    if (inserted) {
      this.__ob__.observeArray(inserted);
    }
    return result;
  };
});

注解

  1. 需要重写的方法是能够修改原数组的方法
  2. 若有有新增的数据,需要调用 Observer类的observeArray 重新观测新增的数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值