介绍
开头继续感谢崔大的mini-vue 项目。
强烈建议大家把项目 down 下来,运行一下,看VUE3 的整体运行逻辑。
vue2的响应式的逻辑是使用Object.defineProperty 这个函数来进行监听。vue3则是使用了proxy 这个更强大的代理器,提供了多达十三种监听方法。具体可以去看阮一峰的教程
今天我们就来实现 effect 和reactive 并且我们实现依赖收集还有触发依赖。
实现reactive
我们先创建一个reactive 文件,然后在文件里面导出一个reactive 方法。
首先是我们实现一下获取值的方法,传入的是一个目标对象 target,还有对象的 key。
我们可以使用Reflect.get的方式,得到对应的值,然后直接 return 出去。
设置值的也是类似,传入的是目标 target,想要修改的 key,还有对应的值。
我们也是使用Reflect.set 的方式来设置。
export function reactive(raw) {
return new Proxy(raw, {
get(target, key) {
const res = Reflect.get(target, key)
// TODO:收集依赖
return res
},
set(target, key, value) {
const res = Reflect.set(target, key, value)
// TODO:触发依赖
return res
},
})
}
这样子就完成了一个基本上的响应式数据。可以获取值,设置值。
接下来就是在获取值的时候实现我们的依赖收集,在设置值的时候去触发依赖。
依赖收集
依赖收集的核心逻辑就是把目标对象的 key 给收集起来,然后放到一个容器里面。
关系链条就是,我们的 target 目标对象对应 key,key 对应 dep。
首先声明一个targetMap,这个用来存放 target 的。
然后我们使用 depsMap 在targetMap里面取出值。
如果depsMap 不存在的话,我们先它存起来。
dep 也是一样的,如果没有就初始化一下,存储起来。
最后我们需要把用户需要执行的函数-fn 给存到dep 里面
const targetMap = new Map();
export function track(target, key) {
// target -> key -> dep
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);
}
实现effect
class ReactiveEffect {
private _fn: any;
constructor(fn) {
this._fn = fn;
}
run() {
activeEffect = this;
this._fn();
}
}
let activeEffect;
export function effect(fn) {
// fn
const _effect = new ReactiveEffect(fn);
_effect.run();
}
effect最重要就是保存用户传进来的 fn。
在这里使用的是一个类的实现,把逻辑再抽离一层。
然后实例化这个类,执行类的函数run。run 里面把全局变量 activeEffect 赋值等于用户传入的函数。并且执行用户传入进来的 fn。
触发依赖
export function trigger(target, key) {
let depsMap = targetMap.get(target);
let dep = depsMap.get(key);
for (const effect of dep) {
effect.run();
}
}
触发依赖,主要是根据 target 和key 取出收集到的 fn,然后执行。
首先是用 target 从targetMap 取出我们的 depsMap
然后再用 key 从targetMap取出 dep。
然后使用 for 循环,执行 dep 里面的 effect,然后执行里面的 run 方法。
结尾
项目已经放到我的 GitHub 上面了,欢迎大家去start。
我的项目地址:github.com/moyuhaokan/…
再次推荐崔大的项目:github.com/cuixiaorui/…