想要理解vue3的响应式是如何实现的,就需要学习effect函数的相关内容,百闻不如一练,这篇文章咱们一块学习effect函数手撕代码的实现。
开端
声明一个响应式对象,通过effect函数对响应式对象进行包裹,这部分代码如下图:
先看声明响应式reactive里面的内容,它返回一个代理对象,并通过Reflect实现了get和set方法,如下图:
然后再看effect函数的具体内容,在effect中利用面向对象思想,定义一个ReactiveEffect类,类中定义接收依赖的变量,并在构造方法中获取到相应的依赖,声明run函数用来执行依赖,最后抛出接收了依赖参数的effect函数,并调用run函数执行依赖,如下图:
下面来看收集依赖的track函数和触发依赖的trigger函数的实现。
收集依赖
track函数用map来建立target和depsMap之间的映射关系,在收集依赖的时候获取到当前对象,并进行对象是否为空的判断,如果为空就进行一次赋值,用set来建立key和dep之间的映射关系,也进行判空操作,之后在dep中储存响应式依赖,如下图:
触发依赖
在trigger函数中,根据target和key获取相应的dep,通过for of遍历dep中的依赖,调用run函数触发依赖,如下图:
完整代码
effect.spec.ts
import { reactive } from "../reactive";
import { effect } from "../effect"
describe("effect", () => {
it.skip("happy path", () => {
const user = reactive({
age: 20,
});
let nextAge;
effect(() => {
nextAge = user.age + 1;
});
})
})
effect.ts
class ReactiveEffect {
private _fn: any;
constructor(fn) {
this._fn = fn;
}
run() {
activeEffect = this;
this._fn();
}
}
const 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) {
dep = new Set();
depsMap.set(key, dep);
}
dep.add(activeEffect);
}
export function trigger(target, key) {
let depsMap = targetMap.get(target)
let dep = depsMap.get(key)
for(const effect of dep) {
effect.run();
}
}
let activeEffect;
export function effect(fn) {
const _effect = new ReactiveEffect(fn);
_effect.run();
}
reactive.spec.ts
import { reactive } from "../reactive";
describe('reactive', () => {
it('happy path', () => {
const original = { foo:1 };
const observed = reactive(original);
});
});
reactive.ts
import { track, trigger } from './effect'
export function reactive (raw) {
return new Proxy(raw, {
get(target,key){
const res = Reflect.get(target,key);
track(target, key);
return res;
},
set(target, key, value) {
const res = Reflect.set(target, key, value);
trigger(target, key)
return res;
}
})
}