vue3的响应式数据核心是reactive,reactive的核心是effect
下面来实现上图的流程
实现reactive
// reactive.ts
export function reactive(raw) {
return new Proxy(raw, {
get(target, key) {
let res = Reflect.get(target, key);
// 依赖收集
track(target, key);
return res;
},
set(target, key, value) {
let res = Reflect.set(target, key, value);
// 触发依赖
trigger(target, key);
return res;
},
})
}
这样就很简单的实现了一个有get、set操作的reactive函数
通过以下单元测试可以验证上面的代码
// reactive.spec.ts
describe("reactive", () => {
it("happy path", () => {
const original = { foo: 1 };
const observed = reactive(original);
expect(observed).not.toBe(original);
expect(observed.foo).toBe(1);
});
});
实现effect
// effect.ts
class ReactiveEffect {
private _fn;
constructor(fn) {
this._fn = fn;
}
run() {
this._fn();
}
}
export function effect(fn) {
let _effect = new ReactiveEffect(fn);
// effect初始直接执行fn
_effect.run();
}
实现依赖收集
既然要依赖收集,依赖收集起来应该存放在哪里
// effect.ts
class ReactiveEffect {
private _fn;
constructor(fn) {
this._fn = fn;
}
run() {
// 调用run 证明是正在执行的状态
activeEffect = this;
this._fn();
}
}
let targetMap = new Map();
export function track(target, key) {
let depsMap = targetMap.get(target);
if(!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if(!dep) {
// fn是唯一的 所以这里用Set
dep = new Set();
depsMap.set(key, dep);
}
// activeEffect 是当前effect实例对象
dep.add(activeEffect);
}
let activeEffect;
export function effect(fn) {...}
实现触发依赖
// effect.ts
export function trigger(target, key) {
let depsMap = targetMap.get(target);
let dep = depsMap.get(key);
for(let effect of dep) {
effect.run();
}
}
通过以下单元测试来验证
// effect.spec.ts
describe('effect',() => {
it('happy path', () => {
let user = reactive({ age: 10 });
let nextAge;
effect(() => {
nextAge = user.age + 1;
});
expect(nextAge).toBe(11);
user.age++;
expect(nextAge).toBe(12);
})
})