Vue 3 响应式全流程演示
WeakMap 依赖收集、effect 调度、微任务队列与组件渲染
// 🎯 模拟 Vue 3 响应式全流程
console.log("=== Vue 3 响应式全流程演示 ===");
// 1. 模拟 Vue 内部数据结构
const targetMap = new WeakMap();
let activeEffect = null;
const queue = new Set();
let isFlushing = false;
// 2. 模拟响应式数据
const state = { count: 0 };
const reactiveState = new Proxy(state, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, value) {
console.log(`🎯 Proxy setter 拦截: ${key} = ${value}`);
const oldValue = target[key];
target[key] = value;
if (oldValue !== value) {
trigger(target, key);
}
return true;
},
});
// 3. 依赖追踪系统
function track(target, key) {
if (!activeEffect) return;
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);
console.log(` 📍 追踪依赖: ${key} -> ${activeEffect.id}`);
}
function trigger(target, key) {
console.log(` 🔔 trigger 被调用: ${key}`);
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach((effectFn) => {
if (effectFn.scheduler) {
console.log(` 🎯 调用 effectFn.scheduler()`);
effectFn.scheduler(effectFn);
}
});
}
}
// 4. 异步更新队列
function queueJob(job) {
console.log(` 📦 queueJob: ${job.id}`);
if (!queue.has(job)) {
queue.add(job);
console.log(` ✅ 任务加入队列,当前队列大小: ${queue.size}`);
queueFlush();
} else {
console.log(` ⏭️ 任务已存在,跳过`);
}
}
function queueFlush() {
if (!isFlushing) {
isFlushing = true;
console.log(" 🚀 安排微任务执行 flushJobs");
Promise.resolve().then(flushJobs);
}
}
function flushJobs() {
console.log("\n=== 🎉 微任务阶段开始 ===");
console.log(" ⚡ flushJobs 执行队列");
try {
queue.forEach((job) => {
console.log(` 🎨 执行: ${job.id}`);
job.run();
});
} finally {
queue.clear();
isFlushing = false;
}
console.log("=== 🎊 微任务阶段结束 ===\n");
}
// 5. Effect 系统
let effectId = 0;
function effect(fn, options = {}) {
const effectFn = () => {
try {
activeEffect = effectFn;
return fn();
} finally {
activeEffect = null;
}
};
effectFn.id = ++effectId;
effectFn.run = effectFn;
effectFn.scheduler = options.scheduler;
effectFn.deps = [];
console.log(`📝 创建 effect #${effectFn.id}`);
effectFn(); // 初始执行建立依赖
return effectFn;
}
// 6. 模拟组件渲染函数
const componentUpdateFn = () => {
console.log(` 🖼️ 组件渲染: count = ${reactiveState.count}`);
};
// 7. 创建组件 Effect
console.log("\n--- 创建组件响应式Effect ---");
const componentEffect = effect(componentUpdateFn, {
scheduler: (effectFn) => {
console.log(` 🎛️ 调度器被调用,effect #${effectFn.id}`);
queueJob(effectFn);
},
});
// 8. 测试三次数据修改
console.log("\n--- 开始三次数据修改 ---");
function updateThreeTimes() {
console.log("1. 第一次修改: count = 1");
reactiveState.count = 1;
console.log("\n2. 第二次修改: count = 2");
reactiveState.count = 2;
console.log("\n3. 第三次修改: count = 3");
reactiveState.count = 3;
console.log("\n--- 同步代码执行完毕 ---");
}
updateThreeTimes();
// 响应式原理流程(简述)
// v3 通过 Proxy 与 ref 实现依赖追踪与拦截;
// 数据改变 -> trigger -> scheduler -> queueJob -> 微任务 flush -> run -> 渲染更新。
componentUpdateFn 执行流程
render → diff → patch 的关键步骤与实例状态更新
console.log("=== componentUpdateFn 详细执行流程 ===");
// 模拟 Vue 组件实例
const instance = {
data: { count: 0, message: "Hello" },
isMounted: false,
subTree: null,
container: document.createElement("div"),
// 🎯 组件的 render 函数(就是 componentUpdateFn 的核心)
render() {
console.log(" 📝 render() 执行: 读取响应式数据");
// 这里会访问响应式数据,建立依赖关系
const count = this.data.count;
const message = this.data.message;
// 生成 Virtual DOM
return {
type: "div",
props: { class: "container" },
children: [
{ type: "h1", children: `Count: ${count}` },
{ type: "p", children: message },
{ type: "button", props: { onClick: () => this.data.count++ }, children: "Add" }
],
};
},
};
// 模拟 Diff 算法
function diff(oldVNode, newVNode) {
console.log(" 🔍 Diff 算法对比:");
const patches = [];
if (!oldVNode) {
patches.push({ type: "CREATE", vnode: newVNode });
console.log(" - 创建新节点");
return patches;
}
// 简单的属性对比
if (oldVNode.children !== newVNode.children) {
patches.push({ type: "UPDATE_TEXT", oldText: oldVNode.children, newText: newVNode.children });
console.log(` - 文本更新: "${oldVNode.children}" → "${newVNode.children}"`);
}
// 属性对比
const propPatches = diffProps(oldVNode.props, newVNode.props);
if (propPatches.length > 0) {
patches.push({ type: "UPDATE_PROPS", patches: propPatches });
}
return patches;
}
function diffProps(oldProps = {}, newProps = {}) {
const patches = [];
// 省略:属性级别的详细对比
return patches;
}
// 模拟 patch 函数
function patch(oldVNode, newVNode, container) {
console.log(" 🏗️ patch() 执行 DOM 操作");
if (!oldVNode) {
// 挂载阶段
console.log(" - 挂载新节点到 DOM");
mount(newVNode, container);
} else {
// 更新阶段
console.log(" - 更新现有 DOM 节点");
update(oldVNode, newVNode);
}
}
function mount(vnode, container) {
// 创建真实 DOM 的逻辑...
console.log(" - 创建元素:", vnode.type);
}
function update(oldVNode, newVNode) {
// 更新 DOM 的逻辑...
console.log(" - 最小化更新 DOM");
}
// 🎯 完整的 componentUpdateFn
const componentUpdateFn = () => {
console.group("🚀 componentUpdateFn 开始执行");
try {
// 1. 执行 render 函数生成新 VNode
console.log("1. 📝 调用 instance.render()");
const newVNode = instance.render.call(instance);
console.log(" 新 VNode:", JSON.stringify(newVNode, null, 2));
// 2. 执行 Diff 和 Patch
console.log("2. 🔄 执行 Diff & Patch");
const patches = diff(instance.subTree, newVNode);
console.log("3. 🏗️ 执行 DOM 更新");
patch(instance.subTree, newVNode, instance.container);
// 3. 更新实例状态
console.log("4. 📊 更新实例状态");
instance.subTree = newVNode;
if (!instance.isMounted) {
instance.isMounted = true;
console.log(" - 标记组件已挂载");
}
console.log("✅ componentUpdateFn 执行完成");
return newVNode;
} catch (error) {
console.error("❌ componentUpdateFn 执行出错:", error);
throw error;
} finally {
console.groupEnd();
}
};
// 测试
console.log("\n--- 第一次执行(挂载)---");
componentUpdateFn();
console.log("\n--- 数据变化后执行(更新)---");
instance.data.count = 5;
instance.data.message = "Updated!";
componentUpdateFn();