1
理解:
-
创建视图的函数(render)和数据之间的关联;
-
当数据发生变化的时候,希望render重新执行;
-
监听数据的读取和修改;
- defineProperty:监听范围比较窄,只能通过属性描述符去监听已有属性的读取和赋值;兼容性更好;(要求监听数据是对象)
- proxy:监听范围更广;兼容性较差,只能兼容支持ES6 的浏览器(要求监听数据是对象)
如何知晓数据对应的函数;
function track(target, key) {console.log(`依赖收集:${key}`, 'color: #f00');
}function trigger(target, key) {console.log(`派发更新:${key}`, 'color: #f00');
}function isObject(value) {return typeof value === 'object' && value !== null;
}
const handlers = {get(target, key) {// 依赖收集track(target, key);return target[key]; // 返回对象的相应属性值},set(target, key, value) {// 派发更新trigger(target, key);// target[key] = value; // 设置对象的相应属性值// return true;// 赋值成功返回true,赋值失败返回false;这里可以使用try catchreturn Reflect.set(target, key, value)// 也可以使用Reflect.set(target, key, value)。它会直接返回true或者false},
}
// 同一个对象,调用两次reactive,会生成不一样的Proxy对象,没有意义
const targetMap = new WeakMap();
function reactive(target) {if (!isObject(target)) {return target; // 如果不是对象,直接返回}if (targetMap.has(target)) {return targetMap.get(target);// 如果已经代理过了,直接返回;}const proxy = new Proxy(target, handlers);targetMap.set(target, proxy);return proxy;
}
const state = reactive({a: 1,b: 2,
});// fn函数中用到了state数据
function fn() {state.a;state.b;
}fn();
state.a++; // 先读取,再赋值
依赖收集:a color: #f00
依赖收集:b color: #f00
依赖收集:a color: #f00
派发更新:a color: #f00
const obj = {a: 1,b: 2,get c() {return this.a + this.b;}
};const state = reactive(obj);
state.c;
这样写的话,依赖收集只能收集到属性c;因为this指向obj;
可以这样操作:
get(target, key, receiver) {// 依赖收集track(target, key);// receiver指的是代理对象return Reflect.get(target, key, receiver) ; // 改变this指向,将this指向为代理对象// return target[key]; // 返回对象的相应属性值
},
下面的用法,只能收集到c,收集不到c1
const obj = {a: 1,b: 2,c: {c1: 1,},
};const state = reactive(obj);
state.c.c1;
可以这样操作
如果访问的属性值还是一个对象,对属性值再次进行代理;
const obj = {
includes: () => {},
indexOf: () => {},
};
const handlers = {get(target, key, receiver) {// 依赖收集track(target, key);// 对于数组来说,无法在代理对象中找到时,去原始数组中重新找一次// const obj = {};// const arr = [1, obj, 3];// const state = reactive(arr);// state.includes(obj);if ((obj.hasOwnProperty(key) && Array.isArray(target)) {return obj[key];}// receiver指的是代理对象const result = Reflect.get(target, key, receiver) ; if (isObject(result)) {return reactive(result);}},
}
简易的模型已经写好;
2 读信息 进行依赖升级
Object.keys和let i in obj用的都是ownKeys;
‘a’ in obj; 用的是has;
obj.a 用的是get;
这里的读不光是通过state.a来读取a属性
还可能通过’e’ in state;来查看’e’属性在不在state中;
const obj = {};
const state = reactive(obj);
'e' in state;
解决办法:新增has方法
const TrackOpTypes = {GET: 'get', // 读取属性值HAS: 'has', // 判断属性是否存在ITERATE: 'iterate', // 迭代对象
};function track(target, trackOpType, key) {console.log(`依赖收集:${key},收集方法:${trackOpType}`, 'color: #f00');
}
const handlers = {get(target, key, receiver) {// 依赖收集track(target, TrackOpTypes.GET, key);// ...},set(target, key, value, receiver) {},has(target, key) {track(target, TrackOpTypes.HAS, key);return Reflect.has(target, key); // 判断对象是否有key属性}
}
同理
const TriggerOpTypes = {SET: 'set', // 设置属性值ADD: 'add', // 添加属性值DELETE: 'delete', // 删除属性
}function trigger(target, triggerOpType, key) {console.log(`派发更新:${key}, 更改方法:${triggerOpType}`, 'color: #f00');
}
还有一种情况
const handlers = {get,set,has,ownKeys(target) {track(target, TrackOpTypes.ITERATE);return Reflect.ownKeys(target); // 返回对象的所有属性名},
}
function track(target, trackOpType, key) {if (trackOpType === TrackOpTypes.ITERATE) {console.log(`依赖收集方法:${trackOpType}`, 'color: #f00');return;}console.log(`依赖收集:${key},收集方法:${trackOpType}`, 'color: #f00');
}
const obj = { a: '1'};
const obj2 = {}
const state = reactive(obj);
const state2 = reactive(obj2);
for (let i in state) {}
Object.keys(state2);
Object.keys和let i in obj用的都是ownKeys;
依赖收集方法:iterate color: #f00
依赖收集方法:iterate color: #f00
3 新增属性
set(target, key, value, receiver) {// 派发更新const type = target.hasOwnProperty(key)? TriggerOpTypes.SET: TriggerOpTypes.ADD;trigger(target, type, key);return Reflect.set(target, key, value, receiver);},
4 删除属性
deleteProperty(target, key) {trigger(target, TriggerOpTypes.DELETE, key);return Reflect.deleteProperty(target, key); // 删除对象的相应属性}
delete state.a;