Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
React 如何比较新旧 Fiber 树来决定更新、插入或删除什么。
协调是 React 在 state 或 props 变化时高效更新 UI 的算法。React 不是重新渲染整个 DOM 树,而是比较先前的虚拟 DOM 树和新渲染的树,计算所需操作的最小数量。它使用两个关键启发式规则将时间复杂度控制在 O(n) 而非 O(n³)。
React 假设:(1) 不同类型的元素产生完全不同的树 — 因此 React 卸载旧子树并挂载全新的。(2) 开发者可以通过 key prop 在渲染之间提示稳定的标识。
当两个元素具有相同类型(如都是 <div>)时,React 保留现有 DOM 节点,只更新变更的属性/props。这避免了不必要地销毁和重建 DOM 节点。
如果类型变更(如 <div> 变为 <span>),React 会完全卸载旧组件树(运行所有清理操作)并重新挂载新树。State 会丢失。
对于子列表,React 通过 key 匹配新旧项。没有 key 时,React 使用位置 — 当项目重新排序时可能导致不必要的卸载。key 让 React 高效地移动现有 DOM 节点。
当 React 遍历两棵树时,它构建一个 effect 列表 — 一个包含待处理工作(更新、插入、删除)的 Fiber 链表。commit 阶段遍历此列表并将变更应用于真实 DOM。
React 协调引擎中的工作单元。每个组件都有一个 Fiber 节点,存储 type、props、state 以及指向父/子/兄弟 Fiber 的指针。
协调过程中 React 应用变更的 current Fiber 树副本。只有在 commit 阶段完成后才会提升为 current。
协调过程中构建的、具有待处理 DOM 变更的 Fiber 扁平链表,在 commit 阶段遍历执行。
当 React 检测到某个 Fiber 的 props 和 context 没有变化时,可以完全跳过该子树的渲染 — 这是一项关键的性能优化。
1// Without keys — position-based matching (bad for reordering)2// Old: [<A/>, <B/>, <C/>]3// New: [<B/>, <A/>, <C/>]4// React sees: position 0 changed (updates A→B), position 1 changed (updates B→A)5// Result: 2 updates + possible state loss67// With keys — identity-based matching (correct)8// Old: [<A key="a"/>, <B key="b"/>, <C key="c"/>]9// New: [<B key="b"/>, <A key="a"/>, <C key="c"/>]10// React sees: same keys, just reordered11// Result: 0 updates, 2 DOM moves — much more efficient
协调使 React 的声明式模型具有实用性。你描述 UI 应该是什么样的,React 找出所需的最小 DOM 变更集。Fiber 架构使协调可以被中断 — React 可以在树的中途暂停协调,稍后再恢复,从而实现 Suspense 和 transitions 等并发特性。
You have a todo list where users can delete items from the middle. Each item has an input field showing its text. After deleting the 2nd item, the 3rd item's input shows the 2nd item's text.
Using `index` as the key means when item 2 is deleted, React thinks item 3 IS item 2 (same key=2). It reuses the old component instance (with stale state) instead of destroying item 2 and moving item 3.
Use a stable unique ID (e.g., `todo.id`) as the key. Now React correctly identifies which component was removed and which should be kept. The reconciliation algorithm can match by identity instead of position.
Takeaway: The reconciliation algorithm's O(n) shortcut relies entirely on keys for list matching. Using array index as key defeats this optimization and introduces state bugs whenever order changes.
You have a conditional render: `isAdmin ? <AdminPanel /> : <UserPanel />`. Switching between admin and user views causes a full re-mount, destroying all form state.
React's reconciliation sees a different component TYPE at the same position. Its heuristic: 'different type = completely different tree'. It destroys the old subtree and builds a new one from scratch — losing all internal state.
If both panels share a similar structure, use the same component with conditional rendering inside. Or use `key` to explicitly control when a component should re-mount vs update. Understanding that React compares types first, then props, helps you design state-preserving UIs.
Takeaway: React's reconciliation compares elements top-down: type first, then key, then props. A type change always triggers a full unmount/remount of the entire subtree — this is by design, not a bug.