先使用Vue3的reactivity的包测试下响应式的效果:
const { reactive, effect } = require("@vue/reactivity");
let a = reactive({ value: 10 });
let b;
effect(() => {
b = a.value + 10;
console.log(b);
});
a.value = 20;
// 20 会先执行一次
// 30 发生变化之后,执行一次
通过依赖收集进行响应触发,在get的时候进行effect函数的收集,在set的时候进行effect函数的触发。
/*
* @Description:
* @Author: zhaodacheng5
* @Date: 2021-05-26 12:26:11
* @LastEditors: zhaodacheng5
* @LastEditTime: 2021-05-26 14:43:36
*/
class Dep {
constructor(val) {
this.effects = new Set();
this._val = val;
}
get value() {
return this._val;
this.depend();
}
set value(val) {
this._val = val;
this.notice();
}
// 收集依赖
depend() {
if (curEffect) this.effects.add(curEffect);
}
//触发依赖
notice() {
this.effects.forEach((effect) => {
effect();
});
}
}
let curEffect; // 当前作用函数
function effectWatch(effect) {
//收集依赖
curEffect = effect;
effect();
curEffect = null;
}
//test 值报错在dep对象里
const dep = new Dep(10);
let b;
effectWatch(() => {
b = dep.value + 10;
console.log("change:"+b);
});
dep.value = 20;
把数据进行分离,使用proxy代理,对象的get和set方法:
/*
* @Description:
* @Author: zhaodacheng5
* @Date: 2021-05-26 12:26:11
* @LastEditors: zhaodacheng5
* @LastEditTime: 2021-05-26 13:54:26
*/
class Dep {
constructor() {
this.effects = new Set();
}
// 收集依赖
depend() {
if (curEffect) this.effects.add(curEffect);
}
//触发依赖
notice() {
this.effects.forEach((effect) => {
effect();
});
}
}
let curEffect; // 当前副作用函数
function effectWatch(effect) {
//收集依赖
curEffect = effect;
effect();
curEffect = null;
}
const targetMap = new Map();
// target - depsMap
// key - dep 一个对象有多个key
function getDep(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 Dep();
depsMap.set(key, dep);
}
return dep;
}
function reactive(raw) {
return new Proxy(raw, {
get(target, key) {
const dep = getDep(target, key);
//依赖收集
dep.depend();
//返回值
return Reflect.get(target, key);
},
set(target, key, value) {
const dep = getDep(target, key);
const result = Reflect.set(target, key, value);
//触发
dep.notice();
return result;
},
});
}
//test
let a = reactive({ value: 10 });
let b;
effectWatch(() => {
b = a.value + 10;
console.log(b);
});
a.value = 20;
参考:
https://dafunk.gitee.io/views/vue/mini-vue-reactive.html https://github.com/mewcoder/codebase/tree/main/mini-vue3 https://github.com/ReySun/vue3-deep-dive-width-evan-you