Q:
不管是React16之前的栈调和器,还是之后的链表调和器,diff的过程都是DFS(深度优先遍历)。BFS(广度优先遍历)时间复杂度也是O(N),为什么不采用BFS呢?
A:
React 的 diff 算法采用深度优先遍历(DFS)而非层序遍历(BFS),主要是为了保证组件生命周期的执行顺序与渲染顺序一致,同时更高效地处理组件的挂载、更新和卸载过程。
React 在调和(Reconciliation)过程中需要遍历虚拟 DOM 树,对新旧节点进行对比。采用 DFS(深度优先遍历) 能够在一次递归中完成从根节点到叶子节点的完整路径处理,使得:
- 生命周期方法按预期顺序执行:如 componentDidMount、componentWillUnmount 等,在 DFS 中可以自然地在进入和退出节点时调用,确保父子组件的生命周期顺序正确。
- 便于中断与恢复(尤其在 Fiber 架构中): DFS 为后续的 Fiber 协调器(可中断的异步更新)奠定了基础。BFS 需要维护队列状态,难以暂停和恢复。
- 实现简单且性能可控:DFS 只需递归栈或显式栈结构即可实现,而 BFS 需要额外的队列存储每一层的节点,内存开销更大,且不利于快速定位更新路径。
- 与 DOM 结构天然匹配:DOM 本身是树形结构,DFS 更符合"从上到下构建,从下到上提交"的渲染逻辑,便于创建和插入真实 DOM 节点。
相比之下,层序遍历(BFS) 虽然能逐层比较,但在实际 UI 渲染中并无明显优势,反而会打乱组件的挂载顺序,导致生命周期混乱,也不利于错误边界(Error Boundary)等机制的实现。