好好学习,天天向上!
感谢各位三连
。
前言
vue响应式原理...
这些类似的话语,想必各位前端朋友都不会陌生吧,如同大家一样,我也会经常听到类似的话,但是即便如此,也只是停留在语言层面,业务流程层面,要想彻底搞懂,也还是差点意思。今天就尝试用代码来实现一下,帮助理解响应式原理(Vue3)。
响应式的业务逻辑
创建一个响应式对象 reactive({count: 0})
// 这是最后我们需要实现的
const state = reactive({ count: 0 });
let newAge;
effect(() => {
newAge = state.count + 1;
});
可以看到 vue3
中暴露出来的reactive API
就是创建响应式的关键,所以我们要实现这个函数,在实现之前,我们还是要先搞清楚,它在内部做了什么。
- 首先,内部会创建一个
Proxy
对象,重写这个对象的set
和get
函数,并返回这个对象。 - 重写
set
和get
,就是为了在这两个阶段,添加自己的逻辑,也就是配合常说的发布订阅模式
,在get
时,收集依赖,set
时,触发依赖。 - 要想响应式,还需要借助一个函数
effect
,该函数的参数也就是当前响应式对象的依赖函数。 - 收集依赖(
track
)和触发依赖(trigger
)。 - 依赖的收集在一个容器(deps)里面,每次执行,将取出对应依赖并执行。
代码实现
reactive
作用:将传入的对象变为响应式
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: boolean = Reflect.set(target, key, value);
// 触发依赖
trigger(target, key);
return res;
},
});
}
effect
作用:初始化执行依赖,对依赖函数进行包装,让每次依赖执行的时候,都是调用 run
// 用来包装依赖函数的类
class ReactiveEffect {
private _fn;
constructor(fn) {
this._fn = fn;
}
run() {
activeEffect = this;
this._fn();
}
}
// 存出 effect 实例
let activeEffect;
export function effect(effectFn) {
// 包装 effectFn
const _effect = new ReactiveEffect(effectFn);
// 第一次执行
_effect.run();
}
track & trigger
作用:track:在get时收集依赖,trigger:在set时触发依赖。
track:
- 定义一个
targetMap
存储对象对应的depsMap
- 定义一个
depsMap
存储对应key
的依赖函数(effect
) - 将对应依赖函数(
activeEffect
)以set
数据格式存储在depsMap
中
triiger:
- 在对应
targetMap
中取出depsMap
- 在对应
depsMap
中取出dep
- 将
dep
内的所有effectFn
执行
const targetMap = new Map();
export function track(target, key) {
// {target: { key: [ effectFn ]}}
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();
}
}
测试结果
describe("effect", () => {
it("happy path", () => {
// initialization
const user = reactive({ age: 22 });
let newAge;
// 依赖函数,在get时,收集,set时触发
effect(() => {
newAge = user.age + 1;
});
expect(newAge).toBe(23);
// update
user.age++;
expect(newAge).toBe(24);
});
});
总结
- Vue3 使用了
Proxy
对象做劫持,重写get
和set
- 在
get
时,存储依赖函数,set
时,触发依赖函数 - 依赖存储是,以
target
对象,存储每个depsMap
(以key
值存储每个[...effectFn]
)
这就是以上全部内容,希望能帮助到你更好理解Vue3响应式原理
参考
以上代码,参考了大佬的开源项目: mini-vue