这边文章主要面向准备着手看vue-next源码,但是无从下手的朋友
- 如何通过chrome + vscode debug Vue3 的测试案例
- reactivity 精简版(只包含了最基本的数据流逻辑)
- 非复刻源码,只注重怎么实现dataFlow
通过chrome + vscode debug Vue3源码
【博客地址】: shancw96.github.io/blogs/2020/…
开启chrome 调试界面 chrome://inspect
- 打开 chrome,并输入地址: chrome://inspect
- 选择 Open dedicated DevTools for Node
在 package.json - script 添加如下命令
...
"scripts": {
...
"test:debug": "node --inspect node_modules/.bin/jest --runInBand"
},
...
开始对测试案例进行debug
npm run test:debug <path_to_test>
example:debug reactivity 模块下的reactive.spec.ts
相对路径(vue-next 根目录):./packages/reactivity/__tests__/reactive.spec.ts
-
开启chrome://inspect设置
-
在测试文件或者源文件(独立模块)中添加
debugger测试模块中添加或者比如独立模块effect
-
执行debug 命令 对某一个测试文件进行debug
npm run test:debug ./packages/reactivity/__tests__/reactive.spec.ts -
自动触发debug
Vue3 响应式流程
-
将待响应式的对象传入 reactive 函数,进行 proxy 代理,主要为 get 和 set
-
为 effect 函数传入对应的回调方法,effect 函数会在初始化的时候执行一次,触发 proxy 的 get 操作,触发 track 操作,对当前依赖进行收集
-
对响应式数据进行 set 操作触发 trigger 操作,执行对应的 effect 列表
数据结构:
代码实现
reactive 功能
reactive 主要是代理对象,实现响应式 reactive: 使用 proxy 代理传入的对象
function reactive(target) {
// 1. 判断是否为对象,proxy 只能代理对象
if (!isObject(target)) return target;
const proxy = new Proxy(target, {
get,
set,
});
return proxy;
}
get 操作:主要为依赖收集,触发 track 功能
let targetMap = new WeakMap();
function get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
// 依赖收集
track(target, key);
// proxy 只代理了当前层的key,如果key 对应的value 是嵌套的,那么需要进行对应的依赖收集
if (isObject(res)) {
reactive(res);
}
return res;
}
function track(object, key) {
// 如果没有d副作用函数,则不进行依赖收集
if (!activeEffect) {
return;
}
// 有副作用函数需要将副作用函数和当前对象的具体key相互关联
// 1.按照对象初始化分组
let depsMap = targetMap.get(object);
if (!depsMap) {
targetMap.set(object, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
//2. key <-> Array<Effect>
if (!dep.has(activeEffect)) {
dep.add(activeEffect);
activeEffect.deps.push(dep);
}
}
set 操作:触发副作用函数列表
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key);
return result;
}
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap.get(key)) {
// 从没有被track
return;
}
const effects = new Set();
// 将所有的非 activeEffect 加入到等待执行队列中
const add = (effectsToAdd) => {
if (!effectsToAdd) return;
effectsToAdd.forEach((effect) => {
if (effect !== activeEffect) {
// question 为什么不能是activeEffect. ans: 参考例1情况,如果不排除activeEffect,就会循环执行trigger
effects.add(effect);
}
});
};
// 将depsMap中 key 对应的effect 复制一份,放到等待执行队列中
if (key !== void 0) {
add(depsMap.get(key));
}
//执行队列
effects.forEach((effect) => effect());
}
effect 功能
effect 是副作用函数,与 vue2 的 Watcher 功能相同,主要为双向绑定触发后执行的函数
activeEffect = undefined;
function effect(fn) {
const effect = createReactiveEffect(fn);
effect();
return effect;
function createReactiveEffect(fn) {
const effect = function reactiveEffect() {
try {
activeEffect = effect;
return fn();
} finally {
activeEffect = undefined;
}
};
effect.deps = [];
return effect;
}
}
完整代码:✈️ 传送门
写在最后:上述代码根据vue3源码进行大量删减,保留最基本的逻辑功能(依赖收集),会存在很多问题,只进行了基本类型的测试,勿杠。