为什么要重写数组方法触发视图更新
如果数据类型是对象的时候,可以使用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);
}
注解
- 当数据类型时object 类型并且 对象没有被观测过防止循环引用
- 需要判断数据是数组还是对象
- 如果是对象,就使用object.defineProperty将对象的key定义为响应式的
- 如果是数组,不能挂测所有的数据,因为数组数据太多消耗性能,所以我们选择重写数通过这些重写的方法触发视图更
数组处理的代码
//先要拿到数组原型上的方法
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;
};
});
注解
- 需要重写的方法是能够修改原数组的方法
- 若有有新增的数据,需要调用 Observer类的observeArray 重新观测新增的数据
|