vue2 - vue3响应式原理代码的实现(二)
一、对象的依赖管理
- 目前是创建了一个Depend对象,用来管理对于name变化需要监听的响应函数;
- 通过WeakMap来管理不同对象的不同依赖关系;
1.1. 对象依赖管理的实现
- 监听对象的变化代码重构
const targetMap = new WeakMap(); const objProxy = new Proxy(obj, { get(target, key, receiver) { return Reflect.get(target, key, receiver); }, set(target, key, newVal, receiver) { Reflect.set(target, key, newVal, receiver); let map = targetMap.get(target); if (!map) { map = new Map(); targetMap.set(target, map); }; let depend = map.get(key); if (!depend) { depend = new Depend(); map.set(key, depend); }; depend.notify(); } });
1.2. 正确的依赖收集
- 收集依赖watchFn代码重构;
- 如果一个函数中使用了某个对象的key,那么它应该被收集依赖;
//原来: //function watchFn(fn) { // depend.addDepend(fn); //}; //更改为: let activefn = null; function watchFn(fn) { activefn = fn; //7.调用函数会触发get fn(); activefn = null; }; // 封装一个获取depend的函数 const targetMap = new WeakMap(); function getDepend(target, key) { let map = targetMap.get(target); if (!map) { map = new Map(); targetMap.set(target, map); }; let depend = map.get(key); if (!depend) { depend = new Depend(); map.set(key, depend); }; return depend; }; const objProxy = new Proxy(obj, { get(target, key, receiver) { const depend = getDepend(target, key); depend.addDepend(activefn); return Reflect.get(target, key, receiver); }, set(target, key, newVal, receiver) { Reflect.set(target, key, newVal, receiver); const depend = getDepend(target, key); depend.notify(); } }); watchFn(function() { console.log(objProxy.name, 'name需要响应的代码块'); }); watchFn(function() { console.log(objProxy.age, 'age需要响应的代码块'); }); objProxy.name = 'ace'; /* yzh name需要响应的代码块 18 age需要响应的代码块 ace name需要响应的代码块 */
二、Depend重构
- 问题一:如果函数中有用到两次key,比如name,那么这个函数会被收集两次;
- 解决:不使用数组,而是使用Set;
- 问题二:我们并不希望将添加reactiveFn放到get中,因为它是属于Dep的行为;
- 解决:addDepend方法优化;
let activefn = null; class Depend { constructor() { // this.reactiveFns = []; this.reactiveFns = new Set(); } // addDepend(reactiveFn) { // this.reactiveFns.push(reactiveFn) // } addDepend() { if (activefn) { this.reactiveFns.add(activefn); } } notify() { this.reactiveFns.forEach(fn => { fn(); }) } } const objProxy = new Proxy(obj, { get(target, key, receiver) { const depend = getDepend(target, key); // depend.addDepend(activefn); depend.addDepend(); return Reflect.get(target, key, receiver); }, set(target, key, newVal, receiver) { Reflect.set(target, key, newVal, receiver); const depend = getDepend(target, key); depend.notify(); } })
三、创建响应式对象
- 目前的响应式是针对于obj一个对象的,我们可以创建出来一个函数,针对所有的对象都可以变成响应式对象;
- 完整代码:
let activefn = null; // 3. class Depend { constructor() { this.reactiveFns = new Set(); } addDepend() { if (activefn) { this.reactiveFns.add(activefn); } } notify() { this.reactiveFns.forEach(fn => { fn(); }) } }; // 1. function watchFn(fn) { activefn = fn; //7.调用函数会触发get fn(); activefn = null; }; // 5.封装一个获取depend函数 const targetMap = new WeakMap(); function getDepend(target, key) { // 7.1.根据target对象获取map,第一次肯定是没有所以设置进去 let map = targetMap.get(target); if (!map) { map = new Map(); targetMap.set(target, map); }; // 根据key获取depend对象,第一次肯定是没有所以设置进去 let depend = map.get(key); if (!depend) { depend = new Depend(); map.set(key, depend); }; return depend; }; function reactive(obj) { return new Proxy(obj, { get(target, key, receiver) { //根据target key获取对应的depend const depend = getDepend(target, key); depend.addDepend(); return Reflect.get(target, key, receiver); }, set(target, key, newVal, receiver) { Reflect.set(target, key, newVal, receiver); // 6.拿到属于变化的depend调用notify const depend = getDepend(target, key); depend.notify(); } }); }; const info = reactive({ name: 'luffy' }); watchFn(() => { console.log(info.name); }); info.name = 'yzh';
四、Vue2响应式原理
- 可以将reactive函数进行如下的重构:
- 在传入对象时,我们可以遍历所有的key,并且通过属性存储描述符来监听属性的获取和修改;
- 在setter和getter方法中的逻辑和前面的Proxy是一致的;
function reactive(obj) { Object.keys(obj).forEach(key => { let value = obj[key]; Object.defineProperty(obj, key, { get() { const depend = getDepend(obj, key); depend.addDepend(); return value; }, set(newVal) { value = newVal; const depend = getDepend(obj, key); depend.notify(); } }) }); return obj; };
转载自:https://juejin.cn/post/7182747796892549178