第一步
先明确依赖收集时候,数据的格式:他是一个 Map 格式的,{ obj: { 属性: Map: { effect1, effect2 } } }
1 2 3 4 5 6 7 8 9 10
| { { name: "xiaoming", age: 18 }: { name: { effect }, age: { effect1, effect2 } } }
|
reactiveEffect.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { activeEffect } from './effect';
const targetMap = new WeakMap();
export function track(target, key) { if (activeEffect) { let depsMap = targetMap.get(target); if (!depsMap) { targetMap.set(target, (depsMap = new Map())); }
let dep = depsMap.get(key); if (!dep) { depsMap.set(key, new Map()); } } }
|
第二步
新增移除映射表中属性所对应的 effect,用于清理不需要的属性
reactiveEffect.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { activeEffect } from './effect';
const targetMap = new WeakMap();
export const createDep = (cleanup, key) => { const dep = new Map() as any; dep.cleanup = cleanup; dep.name = key; return dep; };
export function track(target, key) { if (activeEffect) { let depsMap = targetMap.get(target); if (!depsMap) { targetMap.set(target, (depsMap = new Map())); }
let dep = depsMap.get(key); if (!dep) { depsMap.set(key, dep = createDep(() => depsMap.delete(key), key)); } } }
|
第三步
关联 effect 和收集器
reactiveEffect.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import { activeEffect, trackEffect } from './effect';
const targetMap = new WeakMap();
export const createDep = (cleanup, key) => { const dep = new Map() as any; dep.cleanup = cleanup; dep.name = key; return dep; };
export function track(target, key) { if (activeEffect) { let depsMap = targetMap.get(target); if (!depsMap) { targetMap.set(target, (depsMap = new Map())); }
let dep = depsMap.get(key); if (!dep) { depsMap.set(key, dep = createDep(() => depsMap.delete(key), key)); }
trackEffect(activeEffect, dep); console.log(targetMap); } }
|
effect.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ...
class ReactiveEffect { _trackId = 0; deps = []; _depsLength = 0;
... }
export function trackEffect(effect, dep) {
dep.set(effect, effect._trackId); effect.deps[effect._depsLength++] = dep; }
|
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"></div>
<script type="module"> import { reactive, effect } from './reactivity.js';
let obj = { name: 'xiaoming', age: 18 }; const state = reactive(obj);
effect(() => { app.innerHTML = `姓名${state.name}, 年龄:${state.age}` })
effect(() => { app.innerHTML = `姓名${state.name}` })
setTimeout(() => { state.age++; }, 1000); </script> </body> </html>
|

第四步
触发更新,找到哪个对象上的哪个属性中的哪个 effect,然后让这个 effect 重新执行
触发操作会进入到 set 中
baseHandler.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { ReactiveFlags } from './constants'; import { activeEffect } from './effect'; import { track, trigger } from './reactiveEffect';
export const mutableHandlers: ProxyHandler<any> = { ...
set(target, key, value, receiver) { const oldValue = target[key]; const result = Reflect.set(target, key, value, receiver); if (oldValue !== value) { trigger(target, key, value, oldValue); }
return result; } };
|
reactiveEffect.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export function trigger(target, key, value, oldValue) { const depsMap = targetMap.get(target); if (!depsMap) { return; }
const dep = depsMap.get(key); if (dep) { triggerEffects(dep); } }
|
effect.ts
在最开始创建 effect 对象的时候,effect 对象中 scheduler 保存了用户触发更新的方法,所以在属性再次触发更新的时候,直接调用对应的 scheduler 即可
1 2 3 4 5 6 7
| export function triggerEffects(dep) { for (const effect of dep.keys()) { if (effect.scheduler) { effect.scheduler(); } } }
|
