跳到主要内容

第 36 章:React DevTools 原理——Fiber 探针

React DevTools 能显示 fiber 树、Hooks、渲染耗时。很多人以为它"扒开"了 React 内部。实际正好相反:React 自己开了一扇门,在关键节点主动把数据递出去。本章看这扇门是怎么开的。

先记住这一句:DevTools 不是黑进 React,是 React 主动上报:DevTools 往页面注入一个全局对象(__REACT_DEVTOOLS_GLOBAL_HOOK__),React 启动时注册进去,然后在调度、提交等关键节点主动调它,把 fiber 数据递出去。DevTools 只是"听"。

一图流:探针机制的完整链路

那扇门:REACT_DEVTOOLS_GLOBAL_HOOK

DevTools 注入页面后,在 window 上放一个全局对象:__REACT_DEVTOOLS_GLOBAL_HOOK__ReactFiberDevToolsHook.js:40)。它有一组约定好的方法injectonScheduleFiberRootonCommitFiberRoot……这组方法就是 React 和 DevTools 之间的"协议"。React 不需要知道 DevTools 怎么实现面板,只要按协议调用就行。

React 启动:注册自己

React 初始化时调 injectInternalsReactFiberDevToolsHook.js:50):把"我是哪个渲染器、版本多少、给你一把访问 fiber 的钥匙"打包传进去,hook.inject(internals) 返回一个 rendererID74)。从这以后,DevTools 知道"页面上有 React,id 是 N",并拿着这把钥匙去读 fiber。

如果页面上没有 DevTools(__REACT_DEVTOOLS_GLOBAL_HOOK__ 不存在),injectInternals 直接返回 false,React 正常跑,只是没人"听"。这也是为什么不装 DevTools 时 React 零额外开销:所有探针调用都被这个全局对象是否存在挡住了。

关键节点:主动上报

注册之后,React 在几个关键时机主动调 hook 的方法:

  • onScheduleRoot93):有活被安排时上报,DevTools 能标记"这棵树有 pending 工作"。
  • onCommitRoot111):commit 完成时上报,DevTools 借此知道"树变了",刷新 Components 面板、给 Profiler 记一笔。

onCommitRoot 还会带上这次提交的 fiber 和计时数据(第 8 章那些 profiler 字段:actualDuration 等,dev 构建里才有)。DevTools 的 Profiler 火焰图,数据来源就是这些上报。

DevTools 侧:拿着钥匙自己逛

收到上报后,DevTools 拿着 rendererID 对应的"访问器",自己遍历 fiber 树

  • Components 面板:从 root 往下走 child / sibling(第 3 章三指针),逐个显示组件名、props、Hooks。你看到的列表就是一次 fiber 树的深度优先遍历。
  • Profiler:把每次 onCommitRoot 上报的耗时累计,画成火焰图。

所以 DevTools 不依赖任何 React 内部实现细节,只依赖那组稳定的 hook 方法。这也是为什么 React 大版本升级,DevTools 通常还能用:协议没变,实现随便改。

动手实验

  1. 看全局对象:装了 DevTools 后,在控制台 console.log(window.__REACT_DEVTOOLS_GLOBAL_HOOK__),看它暴露的方法清单(inject、onCommitFiberRoot 等)。
  2. 看 React 上报:在 onCommitRoot(ReactFiberDevToolsHook.js:111)打断点,点一次按钮,看 React 主动调 hook、DevTools 拿到数据的过程。
  3. 验证零开销:禁用 DevTools 扩展,刷新页面,确认 __REACT_DEVTOOLS_GLOBAL_HOOK__ 是 undefined、React 的探针路径全部短路。

小结

  • DevTools 不是黑进 React,是 React 主动上报:全局 Hook(40)+ 启动注册(50)+ 关键节点通知(93/111)。
  • injectInternals 拿 rendererID,onScheduleRoot / onCommitRoot 是主要上报时机。
  • DevTools 拿着钥匙自己遍历 fiber 树(三指针),Components 面板就是一次深度优先遍历。
  • 没装 DevTools 时零开销,所有探针调用被全局对象短路。
  • 协议稳定,React 大版本升级 DevTools 通常仍可用。
  • 正文到此结束。附录见:术语表、packages 地图、版本对照。