Reconciler 运作流程
前端React
暴露调度 API
暴露调度相关 API(如
scheduleUpdateOnFiber),无论是 setState ,还是 root 初次渲染,都会进入这个 scheduleUpdateOnFiber API,都必须走优先级 lanes 系统。React 包通过这些 API 告诉 Reconciler:这个 Fiber 要更新了。
注册调度任务
lane 会向上冒泡到 root,到了 root 之后,Reconciler 把更新请求交给 Scheduler,发起创建 task 的请求。Scheduler 负责创建 task,根据优先级安排执行时机,等待 Scheduler 回调执行。Reconciler 会根据:
- root 是否有待处理的 lanes
- 新旧任务的优先级比较
- 决定用同步还是并发调度
来判断要不要给这个 root 安排,更新一个 Scheduler 任务。
构造 Fiber
执行 Task 回调,Scheduler 在内存中构建新的
Fiber Tree(beginWork / completeWork)。先从 root.current创建或者复用一棵 workInProgress 树,然后遍历 workInProgress ,不断处理每个 fiber 节点的 beginWork / completeWork 循环。beginWork(自上而下,算 children),对不同类型 fiber 做不同事:- FunctionComponent:调用函数组件,拿到
children,做 diff,生成子 fiber - ClassComponent:调用
render(),生成子 fiber - HostComponent(如
div):diff children,生成子 fiber
如果没有子节点了,进入
completeWorkcompleteWork(自下而上,准备 DOM / 标记副作用),对不同类型 fiber 做不同事:- FunctionComponent/ClassComponent:主要是收集 effect
- HostComponent(如
div):在内存中创建 DOM 节点实例,打上update标记,真正改属性在 commit 阶段
最后给
fiber 打上副作用标记,并生成 effect list , 一条独立于 fiber 的单项链表。- Placement(需要插入)
- Update(需要更新属性)
- Deletion(需要删除)
提交到渲染器
- Commit 阶段是不可中断的,Reconciler 会调用 react-dom 包,执行
- 插入 DOM
- 更新 DOM
- 删除 DOM
- 浏览器执行 DOM 更新,但是还没有实际绘制。
- 之后进入
layout阶段,执行componentDidMount,componentDidUpdate,useLayoutEffect等过程,会阻塞渲染。
- 浏览器绘制,用户看到真正的界面更新。
- 最后进入
passive阶段,执行useEffect,不会阻塞渲染。
简化流程如下:
JS 修改 DOM(React mutation) → useLayoutEffect → 浏览器绘制 → useEffect