ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

React Fiber 面试题

React Fiber 面试题 一、Fiber 和旧版 Stack 架构的核心区别是什么核心思路一句话Stack 是“递归同步算到底”Fiber 是“把工作拆开后再调度”。旧版 React 更新组件的时候大概是开始更新 ↓ 递归组件树 ↓ 一直算 ↓ 算完 ↓ 提交 DOM中间基本停不下来。如果组件树特别大大量计算 ↓ JavaScript 长时间占着主线程 ↓ 用户点击没法及时响应 ↓ 浏览器也没法及时绘制 ↓ 页面卡顿Fiber 改成开始更新 ↓ 拆成很多小工作 ↓ 做一部分 ↓ 看看现在要不要停 ↓ 需要停 → 先让其他任务执行 ↓ 之后再回来继续所以 Fiber 最核心的变化就是让 React 的渲染工作变得可以被调度而不是一口气执行到底。第二层Fiber 到底是什么核心思路一句话Fiber 本质上就是 React 内部的一个 JavaScript 对象它记录了组件的节点信息和状态。可以简单理解成Fiber ├── 节点信息 ├── 父子兄弟关系 ├── props / state ├── 对应的 DOM ├── 更新优先级 └── 协调过程中的其他状态二、Fiber 为什么能够中断渲染核心思路一句话Fiber 能中断的根本原因是它把原来存在递归调用栈里的执行进度变成了 React 自己可以保存和管理的状态。这样 React 就不用一口气递归到底做到一个工作单元后可以先停下来之后再接着做。大概是这样Fiber A ↓ Fiber B ↓ Fiber C ↓ Fiber DReact 会不断处理处理 Fiber ↓ 还有工作吗 ↓ 现在适合继续吗 ├── 是 → 继续 └── 否 → 暂停Fiber 让 React 知道“我现在做到哪里了”所以可以暂停以后再继续。三、这里的“中断”到底是什么意思这个地方很容易被面试官追问。核心思路一句话不是把一个 JavaScript 函数执行到一半强行暂停而是在一个工作单元完成之后决定要不要继续做下一个。比如functiondoWork(){// 一次工作}React 不能做到执行 doWork() ↓ 执行到第 50 行 ↓ 强行暂停 JavaScript ↓ 浏览器先去干别的不是这个意思。实际上更接近处理 Fiber A ↓ 处理完成 ↓ 检查现在要不要继续 ↓ 暂停 ↓ 以后继续处理 Fiber B所以面试的时候最好直接说Fiber 的可中断是工作单元之间可以中断而不是 JavaScript 指令执行到一半可以被强制暂停。这个回答就比较到位。四、React 怎么知道“现在应该暂停”核心思路一句话React 在工作循环里会不断检查当前任务是不是应该让出执行机会如果需要就先停下来剩下的工作以后再做。可以简单理解成while(nextUnitOfWork){if(shouldYield()){break;}nextUnitOfWorkperformUnitOfWork(nextUnitOfWork);}这里面最重要的两个东西nextUnitOfWork ↓ 我现在做到哪个 Fiber 了 shouldYield() ↓ 现在还要不要继续所以 Fiber 能暂停本质上依赖工作可以拆开 工作进度可以保存 React 可以决定什么时候继续五、那 React 是怎么保存“做到哪里了”的核心思路一句话React 内部会维护当前工作进度记录下一步要处理的 Fiber所以暂停之后它知道从哪里继续执行。然后再补一句原理Fiber 本身通过child、sibling、return等关系把组件树的遍历路径串起来而 React 内部的工作指针记录下一步要处理哪个 Fiber。因此执行到一半时可以停下来之后继续处理剩下的工作。所以真正的重点就是Fiber 节点 ↓ 记录“树怎么往下走” child / sibling / return ↓ React 工作循环 ↓ 记录“下一步处理谁” ↓ 暂停 ↓ 之后继续如果面试官问“那 React 到底怎么知道暂停后从哪里继续”直接回答因为 React 内部会保存下一步要处理的 Fiber 引用暂停时这个工作位置不会丢恢复之后继续处理这个 Fiber。这才是这个问题最直接的答案。六、如果这时候来了一个更重要的更新呢核心思路一句话高优先级更新产生 → React 给更新标记优先级 → 调度器发现有更高优先级工作 → 工作循环在可让出的检查点停止当前 Render → 保存当前工作进度 → 切换到高优先级任务 → 高优先级任务完成后再决定是否继续之前的低优先级工作。例如低优先级 Render ↓ Fiber A ↓ Fiber B ↓ Fiber C ↓ 发现高优先级更新 ↓ 停止继续计算低优先级 Render ↓ 保存当前工作进度 ↓ 开始处理高优先级更新 ↓ 完成高优先级 Render ↓ Commit这就是大家经常说的高优先级更新可以打断低优先级更新。七、React 怎么知道哪个更新更重要核心思路一句话React 会给不同更新分配不同的优先级现在主要通过 Lane 来表示这些更新的优先级和批次关系。简单理解Update ↓ Lane ↓ 表示这个更新的重要程度 ↓ React 根据优先级安排工作比如用户输入 ↓ 通常需要尽快响应 Transition 更新 ↓ 可以晚一点处理所以不是所有 Update ↓ 都排成一条队 ↓ 一个一个死磕到底而是会根据优先级安排处理顺序。八、那 Fiber 是不是“可以取消”核心思路一句话严格来说不要简单说“Fiber 可以取消”更准确的是一个还没提交的 Render 工作可以被打断之前算出来的结果可以不要然后重新计算。例如低优先级 Render A ↓ 做到一半 ↓ 来了高优先级 Update B ↓ A 被打断 ↓ 先处理 B ↓ A 之前算出来但还没提交的结果 ↓ 可以直接丢掉所以高级面试里最好说Render 阶段可以被打断和重新计算未提交的计算结果可以被丢弃。比直接说Fiber 可以取消。更加准确。九、Render 为什么可以中断但 Commit 不行这是 Fiber 非常重要的一个追问。核心思路一句话Render 只是计算“页面最后应该长什么样”可以反复算Commit 是真正修改 DOM必须保证结果完整地提交。可以把 React 更新分成两个阶段Render ↓ 计算最终结果 ↓ 可以暂停 可以重新计算 可以丢弃 ↓ Commit ↓ 真正修改 DOM 执行副作用 ↓ 需要保持一致性所以面试时直接说Fiber 真正可中断的主要是 Render 阶段Commit 阶段需要保持原子性不能像 Render 一样随意中断。十、Fiber 是不是用了 requestIdleCallback这个问题非常容易答错。核心思路一句话不要把 Fiber 的中断简单理解成“使用 requestIdleCallback 每处理一个 Fiber 就让出主线程”。这种说法太简单而且容易过时。正确理解应该是Fiber ↓ 提供可拆分的工作单元 ↓ React 工作循环 ↓ Scheduler 负责调度 ↓ 根据当前情况决定什么时候继续、什么时候让出所以Fiber ≠ requestIdleCallback更不能说处理一个 Fiber ↓ requestIdleCallback ↓ 处理下一个 Fiber实际情况是 React 可以在一次调度中连续处理多个工作单元然后根据调度情况决定是否让出。十一、Fiber 最终解决的到底是什么问题核心思路一句话Fiber 不是让 React 的计算量凭空减少而是让大量 UI 计算可以被拆开和调度避免一次更新长时间霸占主线程。完整链路组件更新 ↓ 产生大量 UI 工作 ↓ Fiber 把工作拆开 ↓ React 工作循环处理 Fiber ↓ 根据调度情况决定是否继续 ↓ 必要时暂停 ↓ 高优先级更新可以先处理 ↓ 低优先级工作之后继续 / 重新计算 ↓ Render 完成 ↓ Commit ↓ 真正更新 DOM十二、这道题真正的核心矛盾主要矛盾大型 React 更新可能长时间占用主线程导致用户交互和浏览器绘制得不到及时处理。Fiber 的解决办法一次性递归执行 ↓ 改成 ↓ 可拆分的工作 ↓ 可保存进度 ↓ 可调度 ↓ 可暂停 / 恢复 / 重新计算次要矛盾优先级怎么管理 ↓ Lane 什么时候暂停 ↓ Scheduler 工作循环 暂停以后怎么继续 ↓ 保存 Fiber 工作状态 之前算的结果怎么办 ↓ 未提交结果可以丢弃 最终什么时候修改 DOM ↓ Commit十三、面试口语版如果面试官问“Fiber 和旧版 Stack 架构有什么区别”你可以直接这样回答简单说旧版 React 是递归同步地一口气把更新算完Fiber 则是把这个过程拆成一个个可以调度的工作单元所以 React 可以根据情况暂停、继续或者优先处理其他更新。Fiber 本身就是 React 内部表示组件节点和协调状态的数据结构它让 React 不再完全依赖 JavaScript 的递归调用栈保存工作进度。有了 Fiber 以后React 可以不断处理一个个 Fiber并检查当前是不是应该继续。如果正在处理低优先级更新这时候来了更重要的更新就可以先暂停当前 Render优先处理新的更新之前还没有提交的计算结果也可以丢掉重新算。这里要注意所谓“可中断”不是把一个 JavaScript 函数执行到一半强行暂停而是一个工作单元完成之后可以决定要不要继续下一个。另外 React 的 Render 和 Commit 要分开看Render 主要是在计算最终结果可以中断、重做Commit 才是真正修改 DOM需要保证提交过程的一致性。所以 Fiber 真正解决的不是让 React 计算得更快而是让 React 的 UI 计算变得可拆分、可调度避免大型更新长时间占用主线程从而让用户交互和页面绘制更及时。
返回列表