普通视图

发现新文章,点击刷新页面。
今天 — 2025年12月27日首页

Vue.js 源码揭秘(一):Vue3 架构总览

作者 借个火er
2025年12月27日 13:51

Vue.js 源码揭秘(一):Vue3 架构总览

本文从全局视角解析 Vue3 的核心架构,建立源码阅读的整体认知。

一、整体架构

┌─────────────────────────────────────────────────────────────┐
│                      Vue Application                        │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                    Compiler (编译时)                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   Parse     │─►│  Transform  │─►│   Codegen   │         │
│  │  (解析)     │  │   (转换)    │  │  (代码生成)  │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼ render function
┌─────────────────────────────────────────────────────────────┐
│                    Runtime (运行时)                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ Reactivity  │  │   Renderer  │  │  Scheduler  │         │
│  │  (响应式)   │  │   (渲染器)   │  │   (调度器)   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                      DOM / Platform                         │
└─────────────────────────────────────────────────────────────┘

二、编译时 vs 运行时

2.1 编译时(Compile Time)

// 模板
<template>
  <div>{{ msg }}</div>
</template>

// 编译后的 render 函数
function render(_ctx) {
  return _createElementVNode("div", null, _toDisplayString(_ctx.msg))
}

2.2 运行时(Runtime)

// 运行时执行 render 函数
const vnode = render(ctx)

// patch 到 DOM
patch(null, vnode, container)

三、响应式系统

3.1 核心 API

// reactive - 对象响应式
const state = reactive({ count: 0 })

// ref - 基本类型响应式
const count = ref(0)

// computed - 计算属性
const double = computed(() => count.value * 2)

// effect - 副作用
effect(() => {
  console.log(count.value)
})

3.2 依赖收集与触发

┌─────────────────────────────────────────────────────────────┐
│                    响应式流程                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────┐    get     ┌─────────┐                       │
│   │  Proxy  │ ─────────► │  track  │ ──► 收集当前 effect    │
│   └─────────┘            └─────────┘                       │
│        │                                                    │
│        │ set                                                │
│        ▼                                                    │
│   ┌─────────┐            ┌─────────┐                       │
│   │ trigger │ ─────────► │ effects │ ──► 执行所有 effect    │
│   └─────────┘            └─────────┘                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.3 Dep 与 Effect

// Dep - 依赖容器
class Dep {
  subs: Set<Subscriber>  // 订阅者集合
  
  track() {
    if (activeSub) {
      this.subs.add(activeSub)
    }
  }
  
  trigger() {
    this.subs.forEach(sub => sub.notify())
  }
}

// ReactiveEffect - 副作用
class ReactiveEffect {
  deps: Link[]  // 依赖链表
  
  run() {
    activeSub = this
    return this.fn()
  }
  
  notify() {
    this.scheduler ? this.scheduler() : this.run()
  }
}

四、虚拟 DOM

4.1 VNode 结构

interface VNode {
  type: string | Component    // 节点类型
  props: object | null        // 属性
  children: VNode[] | string  // 子节点
  el: Element | null          // 真实 DOM
  key: string | number        // diff key
  shapeFlag: number           // 节点类型标记
  patchFlag: number           // 优化标记
}

4.2 ShapeFlags

enum ShapeFlags {
  ELEMENT = 1,                    // 普通元素
  FUNCTIONAL_COMPONENT = 1 << 1,  // 函数组件
  STATEFUL_COMPONENT = 1 << 2,    // 有状态组件
  TEXT_CHILDREN = 1 << 3,         // 文本子节点
  ARRAY_CHILDREN = 1 << 4,        // 数组子节点
  SLOTS_CHILDREN = 1 << 5,        // 插槽子节点
  TELEPORT = 1 << 6,              // Teleport
  SUSPENSE = 1 << 7,              // Suspense
  COMPONENT = STATEFUL_COMPONENT | FUNCTIONAL_COMPONENT
}

五、渲染器

5.1 patch 函数

const patch = (n1, n2, container) => {
  if (n1 === n2) return
  
  // 类型不同,卸载旧节点
  if (n1 && !isSameVNodeType(n1, n2)) {
    unmount(n1)
    n1 = null
  }
  
  const { type, shapeFlag } = n2
  
  switch (type) {
    case Text:
      processText(n1, n2, container)
      break
    case Fragment:
      processFragment(n1, n2, container)
      break
    default:
      if (shapeFlag & ShapeFlags.ELEMENT) {
        processElement(n1, n2, container)
      } else if (shapeFlag & ShapeFlags.COMPONENT) {
        processComponent(n1, n2, container)
      }
  }
}

5.2 组件挂载

const mountComponent = (vnode, container) => {
  // 1. 创建组件实例
  const instance = createComponentInstance(vnode)
  
  // 2. 设置组件(执行 setup)
  setupComponent(instance)
  
  // 3. 设置渲染副作用
  setupRenderEffect(instance, vnode, container)
}

const setupRenderEffect = (instance, vnode, container) => {
  const effect = new ReactiveEffect(() => {
    if (!instance.isMounted) {
      // 首次挂载
      const subTree = instance.render()
      patch(null, subTree, container)
      instance.subTree = subTree
      instance.isMounted = true
    } else {
      // 更新
      const nextTree = instance.render()
      patch(instance.subTree, nextTree, container)
      instance.subTree = nextTree
    }
  })
  
  effect.run()
}

六、调度器

6.1 任务队列

const queue: SchedulerJob[] = []
let isFlushing = false

function queueJob(job) {
  if (!queue.includes(job)) {
    queue.push(job)
    queueFlush()
  }
}

function queueFlush() {
  if (!isFlushing) {
    isFlushing = true
    Promise.resolve().then(flushJobs)
  }
}

function flushJobs() {
  queue.sort((a, b) => getId(a) - getId(b))
  
  for (const job of queue) {
    job()
  }
  
  queue.length = 0
  isFlushing = false
}

6.2 nextTick

const resolvedPromise = Promise.resolve()

function nextTick(fn?) {
  return fn 
    ? resolvedPromise.then(fn) 
    : resolvedPromise
}

七、组件系统

7.1 组件实例

interface ComponentInternalInstance {
  uid: number                    // 唯一 ID
  type: Component                // 组件定义
  parent: ComponentInternalInstance | null
  
  // 状态
  data: object                   // data()
  props: object                  // props
  setupState: object             // setup() 返回值
  ctx: object                    // 渲染上下文
  
  // 渲染
  render: Function               // render 函数
  subTree: VNode                 // 渲染的 VNode 树
  effect: ReactiveEffect         // 渲染副作用
  
  // 生命周期
  isMounted: boolean
  isUnmounted: boolean
  
  // 生命周期钩子
  bc: Function[] | null          // beforeCreate
  c: Function[] | null           // created
  bm: Function[] | null          // beforeMount
  m: Function[] | null           // mounted
  bu: Function[] | null          // beforeUpdate
  u: Function[] | null           // updated
  bum: Function[] | null         // beforeUnmount
  um: Function[] | null          // unmounted
}

7.2 setup 执行

function setupComponent(instance) {
  const { props, children } = instance.vnode
  
  // 初始化 props
  initProps(instance, props)
  
  // 初始化 slots
  initSlots(instance, children)
  
  // 执行 setup
  const { setup } = instance.type
  if (setup) {
    const setupResult = setup(instance.props, {
      attrs: instance.attrs,
      slots: instance.slots,
      emit: instance.emit,
      expose: instance.expose
    })
    
    handleSetupResult(instance, setupResult)
  }
}

八、编译优化

8.1 PatchFlags

enum PatchFlags {
  TEXT = 1,              // 动态文本
  CLASS = 1 << 1,        // 动态 class
  STYLE = 1 << 2,        // 动态 style
  PROPS = 1 << 3,        // 动态 props
  FULL_PROPS = 1 << 4,   // 有动态 key
  NEED_HYDRATION = 1 << 5,
  STABLE_FRAGMENT = 1 << 6,
  KEYED_FRAGMENT = 1 << 7,
  UNKEYED_FRAGMENT = 1 << 8,
  NEED_PATCH = 1 << 9,
  DYNAMIC_SLOTS = 1 << 10,
  HOISTED = -1,          // 静态提升
  BAIL = -2              // 退出优化
}

8.2 Block Tree

// 编译优化:只追踪动态节点
const _hoisted_1 = createVNode("div", null, "static")

function render() {
  return (openBlock(), createBlock("div", null, [
    _hoisted_1,  // 静态提升
    createVNode("span", null, ctx.msg, PatchFlags.TEXT)  // 动态节点
  ]))
}

九、完整渲染流程

┌─────────────────────────────────────────────────────────────┐
│                    Vue 渲染流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. createApp(App).mount('#app')                            │
│        │                                                    │
│        ▼                                                    │
│  2. 创建 VNode                                              │
│        │                                                    │
│        ▼                                                    │
│  3. render(vnode, container)                                │
│        │                                                    │
│        ▼                                                    │
│  4. patch(null, vnode, container)                           │
│        │                                                    │
│        ▼                                                    │
│  5. processComponent → mountComponent                       │
│        │                                                    │
│        ├── createComponentInstance                          │
│        ├── setupComponent (执行 setup)                      │
│        └── setupRenderEffect                                │
│              │                                              │
│              ▼                                              │
│  6. ReactiveEffect.run()                                    │
│        │                                                    │
│        ▼                                                    │
│  7. instance.render() → subTree VNode                       │
│        │                                                    │
│        ▼                                                    │
│  8. patch(null, subTree, container)                         │
│        │                                                    │
│        ▼                                                    │
│  9. 递归处理子节点 → 挂载到 DOM                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

十、小结

Vue3 架构的核心:

  1. 响应式系统:基于 Proxy,依赖收集 + 触发更新
  2. 虚拟 DOM:VNode 描述 UI,patch 算法高效更新
  3. 编译优化:PatchFlags、Block Tree、静态提升
  4. 调度器:批量更新,nextTick 微任务队列
  5. 组件系统:setup + Composition API

📦 源码地址:github.com/vuejs/core

下一篇:响应式系统详解

如果觉得有帮助,欢迎点赞收藏 👍

React 19 源码揭秘(二):useState 的实现原理

作者 借个火er
2025年12月27日 10:42

React 19 源码揭秘(二):useState 的实现原理

本文深入 React 源码,带你彻底搞懂 useState 从调用到更新的完整流程。

前言

useState 可能是你用得最多的 Hook,但你知道它背后是怎么工作的吗?

const [count, setCount] = useState(0);
setCount(count + 1);  // 这行代码背后发生了什么?

本文将从源码角度,完整解析 useState 的实现原理。

一、Hook 的数据结构

首先,我们需要了解 Hook 在 React 内部是如何存储的。

Hook 节点

每个 Hook 调用都会创建一个 Hook 对象:

type Hook = {
  memoizedState: any,    // 存储的状态值
  baseState: any,        // 基础状态(用于更新计算)
  baseQueue: Update | null,  // 基础更新队列
  queue: UpdateQueue | null, // 更新队列
  next: Hook | null,     // 指向下一个 Hook
};

Hook 链表

多个 Hook 以链表形式存储在 Fiber 节点的 memoizedState 上:

Fiber.memoizedState
        │
        ▼
    ┌───────┐     ┌───────┐     ┌───────┐
    │ Hook1 │ ──► │ Hook2 │ ──► │ Hook3 │ ──► null
    │useState│     │useEffect│    │useMemo│
    └───────┘     └───────┘     └───────┘

这就是为什么 Hook 不能在条件语句中调用——React 依赖调用顺序来匹配 Hook。

二、首次渲染:mountState

当组件首次渲染时,useState 会调用 mountState

// 源码位置:react-reconciler/src/ReactFiberHooks.js

function mountState(initialState) {
  // 1. 创建 Hook 节点,加入链表
  const hook = mountWorkInProgressHook();
  
  // 2. 处理初始值(支持函数式初始化)
  if (typeof initialState === 'function') {
    initialState = initialState();
  }
  
  // 3. 保存初始状态
  hook.memoizedState = hook.baseState = initialState;
  
  // 4. 创建更新队列
  const queue = {
    pending: null,           // 待处理的更新
    lanes: NoLanes,
    dispatch: null,          // setState 函数
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: initialState,
  };
  hook.queue = queue;
  
  // 5. 绑定 dispatch 函数(就是 setState)
  const dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);
  queue.dispatch = dispatch;
  
  // 6. 返回 [state, setState]
  return [hook.memoizedState, dispatch];
}

mountWorkInProgressHook

这个函数负责创建 Hook 节点并维护链表:

function mountWorkInProgressHook() {
  const hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null,
  };

  if (workInProgressHook === null) {
    // 第一个 Hook,挂载到 Fiber
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // 追加到链表末尾
    workInProgressHook = workInProgressHook.next = hook;
  }
  
  return workInProgressHook;
}

三、触发更新:dispatchSetState

当你调用 setCount(1) 时,实际执行的是 dispatchSetState

function dispatchSetState(fiber, queue, action) {
  // 1. 获取更新优先级
  const lane = requestUpdateLane(fiber);
  
  // 2. 创建更新对象
  const update = {
    lane,
    action,              // 新值或更新函数
    hasEagerState: false,
    eagerState: null,
    next: null,
  };
  
  // 3. 性能优化:Eager State(提前计算)
  if (fiber.lanes === NoLanes) {
    const currentState = queue.lastRenderedState;
    const eagerState = basicStateReducer(currentState, action);
    update.hasEagerState = true;
    update.eagerState = eagerState;
    
    // 如果新旧状态相同,跳过更新!
    if (Object.is(eagerState, currentState)) {
      return;  // Bailout!
    }
  }
  
  // 4. 将更新加入队列
  enqueueConcurrentHookUpdate(fiber, queue, update, lane);
  
  // 5. 调度更新
  scheduleUpdateOnFiber(root, fiber, lane);
}

Eager State 优化

这是一个重要的性能优化:

const [count, setCount] = useState(0);

// 点击按钮
setCount(0);  // 状态没变,React 会跳过这次更新!

React 会在调度之前就计算新状态,如果和旧状态相同(通过 Object.is 比较),直接跳过整个更新流程。

四、更新渲染:updateState

当组件重新渲染时,useState 会调用 updateState

function updateState(initialState) {
  // useState 本质上是预设了 reducer 的 useReducer
  return updateReducer(basicStateReducer, initialState);
}

// 基础 reducer:支持值或函数
function basicStateReducer(state, action) {
  return typeof action === 'function' ? action(state) : action;
}

updateReducer

这是处理更新的核心逻辑:

function updateReducer(reducer, initialArg) {
  // 1. 获取当前 Hook
  const hook = updateWorkInProgressHook();
  const queue = hook.queue;
  
  // 2. 获取待处理的更新
  const pending = queue.pending;
  
  // 3. 计算新状态
  let newState = hook.baseState;
  if (pending !== null) {
    let update = pending.first;
    do {
      const action = update.action;
      newState = reducer(newState, action);
      update = update.next;
    } while (update !== null);
  }
  
  // 4. 保存新状态
  hook.memoizedState = newState;
  
  // 5. 返回新状态和 dispatch
  return [hook.memoizedState, queue.dispatch];
}

updateWorkInProgressHook

更新时,需要从 current 树复制 Hook:

function updateWorkInProgressHook() {
  // 从 current Fiber 获取对应的 Hook
  let nextCurrentHook;
  if (currentHook === null) {
    const current = currentlyRenderingFiber.alternate;
    nextCurrentHook = current.memoizedState;
  } else {
    nextCurrentHook = currentHook.next;
  }
  
  currentHook = nextCurrentHook;
  
  // 复制 Hook 到 workInProgress
  const newHook = {
    memoizedState: currentHook.memoizedState,
    baseState: currentHook.baseState,
    baseQueue: currentHook.baseQueue,
    queue: currentHook.queue,
    next: null,
  };
  
  // 加入链表...
  return newHook;
}

五、完整流程图

┌─────────────────────────────────────────────────────────┐
│                    首次渲染 (Mount)                      │
├─────────────────────────────────────────────────────────┤
│  useState(0)                                            │
│      │                                                  │
│      ▼                                                  │
│  mountState(0)                                          │
│      │                                                  │
│      ├──► 创建 Hook 节点                                │
│      ├──► 初始化 memoizedState = 0                      │
│      ├──► 创建 UpdateQueue                              │
│      ├──► 绑定 dispatch = dispatchSetState              │
│      │                                                  │
│      ▼                                                  │
│  返回 [0, setCount]                                     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│                    触发更新                              │
├─────────────────────────────────────────────────────────┤
│  setCount(1)                                            │
│      │                                                  │
│      ▼                                                  │
│  dispatchSetState(fiber, queue, 1)                      │
│      │                                                  │
│      ├──► 获取优先级 lane                               │
│      ├──► 创建 Update 对象                              │
│      ├──► Eager State: 计算新状态                       │
│      ├──► 比较新旧状态,相同则 Bailout                   │
│      ├──► 入队更新                                      │
│      │                                                  │
│      ▼                                                  │
│  scheduleUpdateOnFiber() ──► 调度重新渲染               │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│                    重新渲染 (Update)                     │
├─────────────────────────────────────────────────────────┤
│  useState(0)  // 初始值被忽略                           │
│      │                                                  │
│      ▼                                                  │
│  updateState(0)                                         │
│      │                                                  │
│      ▼                                                  │
│  updateReducer(basicStateReducer, 0)                    │
│      │                                                  │
│      ├──► 获取对应的 Hook                               │
│      ├──► 处理 UpdateQueue 中的更新                     │
│      ├──► 计算新状态 = 1                                │
│      │                                                  │
│      ▼                                                  │
│  返回 [1, setCount]                                     │
└─────────────────────────────────────────────────────────┘

六、Dispatcher 切换

React 如何区分 mount 和 update?答案是 Dispatcher 切换

// renderWithHooks 中
ReactSharedInternals.H = 
  current === null || current.memoizedState === null
    ? HooksDispatcherOnMount   // 首次渲染
    : HooksDispatcherOnUpdate; // 更新渲染

// 两个 Dispatcher 的 useState 指向不同函数
const HooksDispatcherOnMount = {
  useState: mountState,
  useEffect: mountEffect,
  // ...
};

const HooksDispatcherOnUpdate = {
  useState: updateState,
  useEffect: updateEffect,
  // ...
};

渲染完成后,切换到 ContextOnlyDispatcher,禁止在组件外调用 Hook:

// 渲染完成后
ReactSharedInternals.H = ContextOnlyDispatcher;

const ContextOnlyDispatcher = {
  useState: throwInvalidHookError,  // 抛出错误
  // ...
};

七、为什么 Hook 不能条件调用?

现在你应该明白了:

// ❌ 错误
if (condition) {
  const [a, setA] = useState(0);  // Hook 1
}
const [b, setB] = useState(0);    // Hook 2 或 Hook 1?

// ✅ 正确
const [a, setA] = useState(0);    // 始终是 Hook 1
const [b, setB] = useState(0);    // 始终是 Hook 2

React 通过遍历链表来匹配 Hook,如果顺序变了,状态就乱了。

八、调试技巧

想要亲自验证?在这些位置打断点:

// 首次渲染
mountState          // react-reconciler/src/ReactFiberHooks.js

// 触发更新
dispatchSetState    // react-reconciler/src/ReactFiberHooks.js

// 重新渲染
updateReducer       // react-reconciler/src/ReactFiberHooks.js

用 Counter 组件测试:

const Counter = () => {
  const [count, setCount] = useState(0);  // 断点这里
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
};

小结

本文深入分析了 useState 的实现原理:

  1. 数据结构:Hook 以链表形式存储在 Fiber.memoizedState
  2. 首次渲染:mountState 创建 Hook 和 UpdateQueue
  3. 触发更新:dispatchSetState 创建 Update,调度渲染
  4. Eager State:提前计算,相同状态跳过更新
  5. 重新渲染:updateReducer 处理更新队列,计算新状态
  6. Dispatcher:通过切换实现 mount/update 的区分

下一篇我们将分析 useEffect 的实现原理,看看副作用是如何被调度和执行的。


📦 配套源码:github.com/220529/reac…

上一篇:React 19 源码全景图

下一篇:useEffect 的实现原理

如果觉得有帮助,欢迎点赞收藏 👍

React 19 源码全景图:从宏观到微观

作者 借个火er
2025年12月27日 10:41

React 19 源码全景图:从宏观到微观

本文是 React 源码系列的总览篇,帮你建立完整的知识框架,后续文章将逐一深入。

一、React 是什么?

一句话:React 是一个将状态映射为 UI 的函数

UI = f(state)

当状态变化时,React 会:

  1. 计算新的 UI(Reconciler)
  2. 调度更新任务(Scheduler)
  3. 将变化应用到 DOM(Renderer)

二、三大核心模块

┌─────────────────────────────────────────────────────────┐
│                        你的代码                          │
│            <App /> → useState → setState                │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                   Scheduler 调度器                       │
│                                                         │
│  • 优先级管理(用户交互 > 动画 > 数据请求)               │
│  • 时间切片(5ms 一片,避免卡顿)                        │
│  • 任务队列(最小堆实现)                                │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                  Reconciler 协调器                       │
│                                                         │
│  • Fiber 架构(可中断的链表结构)                        │
│  • Diff 算法(最小化 DOM 操作)                          │
│  • Hooks 系统(状态和副作用管理)                        │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                   Renderer 渲染器                        │
│                                                         │
│  • ReactDOM(Web)                                      │
│  • React Native(移动端)                               │
│  • React Three Fiber3D)                              │
└─────────────────────────────────────────────────────────┘

三、核心概念速览

1. Fiber

Fiber 是 React 的核心数据结构,每个组件对应一个 Fiber 节点:

FiberNode {
  // 类型信息
  tag,              // 组件类型(函数组件=0,类组件=1,DOM=5)
  type,             // 组件函数或 DOM 标签
  
  // 树结构
  return,           // 父节点
  child,            // 第一个子节点
  sibling,          // 兄弟节点
  
  // 状态
  memoizedState,    // Hooks 链表
  memoizedProps,    // 上次的 props
  
  // 副作用
  flags,            // 标记(插入、更新、删除)
  
  // 双缓冲
  alternate,        // 另一棵树的对应节点
}

2. Lane(优先级)

React 19 使用 31 位二进制数表示优先级:

SyncLane           = 0b0000000000000000000000000000010  // 同步(最高)
InputContinuousLane = 0b0000000000000000000000000001000  // 连续输入
DefaultLane        = 0b0000000000000000000000000100000  // 默认
TransitionLane     = 0b0000000000000000000000010000000  // 过渡
IdleLane           = 0b0010000000000000000000000000000  // 空闲(最低)

3. 双缓冲

React 维护两棵 Fiber 树:

  • current:当前屏幕显示的
  • workInProgress:正在构建的

更新完成后一行代码切换:root.current = workInProgress

四、渲染流程

完整流程图

setState() 
    │
    ▼
scheduleUpdateOnFiber()     ← 标记更新
    │
    ▼
ensureRootIsScheduled()     ← 确保调度
    │
    ▼
scheduleCallback()          ← Scheduler 调度
    │
    ▼
performConcurrentWorkOnRoot() ← 开始渲染
    │
    ├─────────────────────────────────────┐
    │         Render 阶段(可中断)         │
    │                                     │
    │  workLoopConcurrent()               │
    │      │                              │
    │      ▼                              │
    │  performUnitOfWork() ←──┐           │
    │      │                  │           │
    │      ▼                  │           │
    │  beginWork()            │ 循环      │
    │      │                  │           │
    │      ▼                  │           │
    │  completeWork() ────────┘           │
    │                                     │
    └─────────────────────────────────────┘
    │
    ▼
    ├─────────────────────────────────────┐
    │        Commit 阶段(不可中断)        │
    │                                     │
    │  commitBeforeMutationEffects()      │
    │      │                              │
    │      ▼                              │
    │  commitMutationEffects()  ← DOM操作 │
    │      │                              │
    │      ▼                              │
    │  root.current = finishedWork        │
    │      │                              │
    │      ▼                              │
    │  commitLayoutEffects()              │
    │                                     │
    └─────────────────────────────────────┘
    │
    ▼
flushPassiveEffects()       ← useEffect(异步)

Render 阶段

beginWork(向下递归):

  • 根据组件类型处理(函数组件、类组件、DOM 元素)
  • 调用组件函数,执行 Hooks
  • Diff 子节点,创建/复用 Fiber

completeWork(向上回溯):

  • 创建 DOM 节点
  • 收集副作用标记
  • 冒泡 subtreeFlags

Commit 阶段

三个子阶段:

阶段 时机 主要工作
Before Mutation DOM 操作前 getSnapshotBeforeUpdate
Mutation DOM 操作 增删改 DOM
Layout DOM 操作后 useLayoutEffect、componentDidMount

五、Hooks 原理

Hooks 以链表形式存储在 Fiber 的 memoizedState 上:

Fiber.memoizedState → useState → useEffect → useMemo → null

useState 流程

Mount:  mountState() → 创建 Hook → 初始化状态 → 返回 [state, setState]
Update: updateState() → 获取 Hook → 处理更新队列 → 返回 [newState, setState]

useEffect 流程

Mount:  mountEffect() → 创建 Effect → 标记 Passive
Commit: flushPassiveEffects() → 执行销毁函数 → 执行创建函数

六、Diff 算法

React Diff 的三个策略:

  1. 同层比较:不跨层级移动节点
  2. 类型判断:类型不同直接替换
  3. key 标识:通过 key 识别节点

单节点 Diff

key 相同 && type 相同 → 复用
key 相同 && type 不同 → 删除重建
key 不同 → 删除,继续找

多节点 Diff(三轮遍历)

第一轮:从左到右,处理更新
第二轮:处理新增或删除
第三轮:处理移动(Map 查找)

七、Scheduler 调度

优先级

ImmediatePriority   // -1ms,立即执行
UserBlockingPriority // 250ms,用户交互
NormalPriority      // 5000ms,普通更新
LowPriority         // 10000ms,低优先级
IdlePriority        // 永不过期,空闲执行

时间切片

function workLoop() {
  while (task && !shouldYield()) {  // 5ms 检查一次
    task = performTask(task);
  }
  if (task) {
    scheduleCallback(task);  // 还有任务,继续调度
  }
}

八、源码目录

packages/
├── react/                    # React API
│   └── src/ReactHooks.js     # Hooks 入口
│
├── react-reconciler/         # 协调器(核心)
│   └── src/
│       ├── ReactFiber.js           # Fiber 定义
│       ├── ReactFiberWorkLoop.js   # 工作循环
│       ├── ReactFiberBeginWork.js  # 递阶段
│       ├── ReactFiberCompleteWork.js # 归阶段
│       ├── ReactFiberHooks.js      # Hooks 实现
│       ├── ReactFiberCommitWork.js # Commit
│       ├── ReactFiberLane.js       # 优先级
│       └── ReactChildFiber.js      # Diff 算法
│
├── react-dom/                # DOM 渲染器
│
└── scheduler/                # 调度器
    └── src/
        ├── Scheduler.js          # 调度逻辑
        └── SchedulerMinHeap.js   # 最小堆

九、系列文章导航

序号 主题 核心内容
00 调试环境搭建 项目介绍、快速开始
01 架构总览(本文) 三大模块、核心概念
02 useState 原理 Hook 链表、更新队列
03 useEffect 原理 Effect 链表、执行时机
04 Fiber 工作循环 beginWork、completeWork
05 Diff 算法 单节点、多节点 Diff
06 Scheduler 调度器 优先级、时间切片
07 Commit 阶段 三个子阶段、DOM 操作

十、学习建议

  1. 先跑起来:clone react-debug,打断点调试
  2. 从 useState 开始:最简单也最核心
  3. 画流程图:边看边画,加深理解
  4. 写测试组件:验证你的理解

📦 配套源码:github.com/220529/reac…

上一篇:React 源码调试环境搭建

下一篇:useState 的实现原理

❌
❌