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();