一、一些问题① React 会先构建完整 Element Tree 吗不会。但刚开始会执行 App / 构建最外层的 Element 然后接下来的 child 组件是在beginWork中逐层执行Element 是逐层产生的。② 两个 alternate 是一个对象吗不是。是两个 Fiber 实例。③ 旧 workInProgress 会销毁吗不会。对象会复用但数据会重置。根据element node 和 current fiber 比如对旧 workInProgress 进行属性覆盖④ completeWork 会创建 DOM 吗会。但不会把 DOM 插入页面。mount 时会创建离屏 dom 节点树然后 commit 时直接将该树挂载。updata 时仅进行局部更新Render 阶段 ├── current Fiber 与新 Element 协调 ├── 构建 workInProgress Fiber 树 ├── 新节点创建 DOM 实例 ├── 已有节点复用原 DOM 实例 └── 标记 Placement、Update、Deletion 等操作 Commit 阶段 ├── 删除不再需要的 DOM ├── 插入或移动新增 DOM ├── 更新已有 DOM 的属性和文本 └── 将 finishedWork Fiber 树切换为 currentUpdate 时completeWork对已经匹配并复用的 Host Fiber通常不会重新创建 DOM它直接复用stateNode中原来的 DOM。只有本轮新挂载的 Host Fiber 才会创建新 DOM⑤ Commit 才创建 DOM 吗不是。Commit 负责的是将 Render 准备好的变更应用到真实 DOM 树包括插入、删除、移动和更新。⑥ swap 是什么commit 的一个阶段root.current finishedWork;把新的 Fiber 树变成当前树而不是交换 DOM。before mutation ↓ mutation插入、删除、移动、更新真实 DOM ↓ root.current finishedWork swap ↓ layout执行 useLayoutEffect、componentDidMount / componentDidUpdate ↓ passive稍后执行 useEffect⑦ 为什么要先改 DOM再切换 current 因为useLayoutEffect和生命周期需要同时看到已经更新后的真实 DOM已经切换后的新 Fiber 状态。8、fiber 内的 stateNode dom是否已经有指向关系有假如update内部有新增子树那么会先 appendChild 等先将子树拼接然后 commit 的时候再找到其父 fiber 的stateNode 进行 appendChild 。已有的 dom 本身也已经有这层关系但不能在 render 就将拼接的子树接上父 fiber 的 stateNode 因为这会立即影响到页面。到了 mutation 阶段React 处理divFiber的Placement从 divFiber.return 向上寻找 ↓ 找到 section Fiber ↓ parent sectionFiber.stateNode ↓ 取得 divFiber.stateNode ↓ parent.appendChild(divDOM)新增 Fiber 会在 Render 阶段先通过return接入 WIP Fiber 树新增 Host Fiber 的stateNode保存新 DOM但不会提前指向或挂入已有父 DOM。若新增节点内部还有新 Host 子树内部可以在completeWork中先组装它与已有页面 DOM 的连接则在 Commit 的 Placement 中通过局部appendChild或insertBefore完成。completeWork直接通过 DOM API 建立新 DOM 节点之间的真实父子关系树的顶层 DOM 由对应 Host Fiber 的stateNode引用后代同时可以通过 DOM 的childNodes和各自 Fiber 的stateNode找到。只有它与已挂载页面 DOM 的最后连接会推迟到 Commit 阶段。Mount所有 Host Fiber 都是新的 ↓ completeWork 从下向上创建 DOM ↓ 新 Host 父节点吸收新 Host 子节点 ↓ 形成离屏 DOM 子树 ↓ Commit 把顶层 DOM 插入根容器Update只有属性变化复用 Fiber alternate ↓ 复用 stateNode 中的旧 DOM ↓ 不重新组装 ↓ Commit 局部更新属性或文本Update新增一棵 Host 子树新增子树内部 completeWork 中提前组装 新增子树与已有页面父 DOM Commit 中 appendChild / insertBefore二、流程1、总的流程┌──────────────────────────────────────────────────────────┐ │ Trigger / Update │ └──────────────────────────────────────────────────────────┘ root.render(rootElement) setState / dispatch props、context 或 Suspense 变化 │ ▼ 创建并入队 Update │ ▼ 选择更新 Lane │ ▼ markRootUpdated │ ▼ ensureRootIsScheduled │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Prepare │ └──────────────────────────────────────────────────────────┘ renderRootSync / renderRootConcurrent │ ▼ prepareFreshStack │ ▼ createWorkInProgress(root.current, null) │ │ 这里只准备 HostRoot WIP │ 不是提前创建完整 WIP 树 ▼ ┌──────────────────────────────────────────────────────────┐ │ Render Phase │ │ 可中断、可重试、可放弃 │ └──────────────────────────────────────────────────────────┘ performUnitOfWork(WIP Fiber) │ ▼ beginWork(current, WIP) │ ├── HostRoot处理根更新队列得到根 Element │ ├── FunctionComponent执行组件得到 nextChildren │ ├── ClassComponent调用 render得到 nextChildren │ └── HostComponent读取 props.children │ ▼ reconcileChildren │ │ current.child Fibers │ │ 本层 nextChildren / Elements │ ↓ │ 匹配 key 和 type │ ↓ │ create / reuse child WIP Fiber │ ↓ │ 标记 Placement、Deletion 等 ▼ 返回 child WIP │ ▼ 继续 beginWork(child) │ ▼ 到达叶子节点 │ ▼ completeWork(leaf) │ ├── 新 Host 节点创建离屏 DOM ├── 已有 Host 节点复用 DOM准备更新 ├── appendAllChildren组装离屏 DOM 子树 └── bubbleProperties向上汇总 flags │ ├── 有 sibling │ └── beginWork(sibling) │ └── 无 sibling └── completeWork(parent) │ ▼ HostRoot complete │ ▼ finishedWork │ │ 新 WIP Fiber Tree 已完成 │ root.current 仍然是旧树 ▼ ┌──────────────────────────────────────────────────────────┐ │ Commit Phase │ │ 将 Render 的结果应用到宿主环境 │ └──────────────────────────────────────────────────────────┘ before mutation │ ├── 读取 DOM 修改前状态 └── getSnapshotBeforeUpdate │ ▼ mutation │ ├── Placement插入 / 移动 DOM ├── Update更新属性 / 文本 ├── ChildDeletion删除 DOM、卸载组件 ├── 解绑旧 ref └── 执行部分 effect 清理 │ ▼ root.current finishedWork │ │ Fiber current 指针切换 │ 不是整棵 DOM Tree 替换 ▼ layout │ ├── useLayoutEffect setup ├── componentDidMount ├── componentDidUpdate └── 挂载新 ref │ ▼ 浏览器获得 Paint 机会 │ ▼ passive effects │ ├── 旧 useEffect cleanup └── 新 useEffect setup2、一句话流程Update 负责提出变化 ↓ Schedule 负责决定什么时候计算 ↓ beginWork 负责边产生 Element、边协调、边构建 WIP Fiber ↓ completeWork 负责向上收尾、创建离屏 DOM、汇总 flags ↓ Commit mutation 负责修改已挂载 DOM ↓ root.current finishedWork 负责切换 Fiber 当前版本 ↓ Layout Effects 读取最新 DOM ↓ Passive Effects 处理非布局型副作用