在 TanStack Start 中接入 react-scan:两种安装方式与生产环境启用指南
2026/9/13 22:11:05 网站建设 项目流程

在 TanStack Start 中接入 react-scan:两种安装方式与生产环境启用指南

【免费下载链接】react-scanScan and fix React performance issues项目地址: https://gitcode.com/GitHub_Trending/re/react-scan

本篇技术指南聚焦于在TanStack Start(TanStack Router 的 full-stack 框架形态)应用中集成 react-scan 的完整流程,覆盖<script>标签直引与模块导入两种方案,并深入讲解react-scan/all-environments生产环境入口的适用场景。读完本文,你将掌握在app/routes/__rootapp/client两处入口正确初始化扫描的写法、理解"必须在 React 之前导入"这一硬性约束的底层原理,并能依据源码判断扫描器在开发/生产环境下的实际启停逻辑。

适用前提:文中的两种方式均要求项目运行在React 19之上(原文明确标注 "This only works for React 19");react-scan 的 peer 依赖声明为react ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0(见 packages/scan/package.json),但 TanStack Start 场景下的注入路径以 React 19 为准。

一、方式一:通过<script>标签注入

将脚本标签添加到<RootDocument>组件中,该组件位于app/routes/__root

// app/routes/__root import { Meta, Scripts } from "@tanstack/start"; // ... function RootDocument({ children }) { return ( <html> <head> <script src="https://unpkg.com/react-scan/dist/auto.global.js" /> <Meta /> </head> <body> {children} <Scripts /> </body> </html> ); } // ...

这里加载的dist/auto.global.js是 react-scan 的auto 模式产物:从源码 packages/scan/src/auto.ts 可以看到,该入口在IS_CLIENT(浏览器环境)下会直接执行scan()并挂载window.reactScan = scan,因此无需手动调用任何初始化函数,脚本加载即自动开始扫描。

CDN 可用地址

auto.global.js可以通过以下 CDN 获取(完整列表见 CDN 安装指南):

  • JSDelivrhttps://cdn.jsdelivr.net/npm/react-scan/dist/auto.global.js
  • UNPKGhttps://unpkg.com/react-scan/dist/auto.global.js

[!CAUTION] 该方式仅适用于 React 19。

二、方式二:作为模块导入(推荐)

将以下代码添加到app/routes/__root<RootDocument>组件中:

// app/routes/__root // react-scan 必须在 React 与 TanStack Start 之前导入 import { scan } from "react-scan"; import { Meta, Scripts } from "@tanstack/start"; import { useEffect } from "react"; // ... function RootDocument({ children }) { useEffect(() => { // 确保只在 hydration 之后执行 scan({ enabled: true, }); }, []); return ( <html> <head> <Meta /> </head> <body> {children} <Scripts /> </body> </html> ); }

为什么必须在 useEffect 中调用

useEffect的回调只在客户端 hydration 完成后运行,这保证了 react-scan 的初始化发生在应用真正可交互之后,避免在服务端渲染(SSR)阶段触发扫描。从核心实现看,scan 函数 会依次执行setOptions(options)start(),而start()的第一道检查就是if (!IS_CLIENT) return;(见 start 实现)——也就是说,即便在服务端误调用了scan,也会被静默跳过,但将其放在useEffect中依然是 SSR 场景下最稳妥的写法。

为什么必须"在 React 之前导入"

[!CAUTION] React Scan 必须在你的整个项目中先于 React(以及其他 React 渲染器,如 React DOM)导入,因为它需要在 React 访问到 React DevTools 之前先行劫持该 hook。

这一约束的根源在于 react-scan 的劫持机制。主入口 packages/scan/src/index.ts 的第一行就是import 'bippy'——bippy 是一个通过副作用安装__REACT_DEVTOOLS_GLOBAL_HOOK__的库,react-scan 借助它来追踪 Fiber 树的渲染。如果 React 已经先一步读取了 DevTools hook,react-scan 便无法完成拦截,扫描器将无法生效。start()末尾还有一个 5 秒的兜底自检(packages/scan/src/core/index.ts#L491-L497):若 5 秒后检测到 instrumentation 仍未激活,会在控制台输出"[React Scan] Failed to load. Must import React Scan before React runs.",这正是导入顺序错误的典型信号。

备选入口:在 app/client 中初始化

如果你更习惯在客户端引导文件中显式初始化,也可以把scan调用放在app/client

// app/client import { scan } from "react-scan"; // 必须在 React 和 React DOM 之前导入 import { hydrateRoot } from "react-dom/client"; import { StartClient } from "@tanstack/start"; import { createRouter } from "./router"; scan({ enabled: true, }); const router = createRouter(); hydrateRoot(document, <StartClient router={router} />);

这里的关键在于scan({ enabled: true })必须在hydrateRoot之前同步执行——因为水合会立即触发 React 对 DevTools hook 的访问,晚于水合的调用将无法完成劫持。

[!CAUTION] 该方式同样仅适用于 React 19。

三、在两种方式之间如何选择

维度script 标签方式模块导入方式
引入位置app/routes/__root<head>app/routes/__rootuseEffect,或app/client
初始化自动(auto 模式自动调用scan()手动调用scan({ enabled: true })
初始化时机脚本加载即生效严格在 hydration 之后 / 水合之前
可控性低(无法传参)高(可传完整Options
适用场景快速验证、临时调试需要精细配置(日志、工具栏、动画速度等)的日常开发

四、让 react-scan 在生产环境运行

默认情况下,scan()不会在生产环境启动。这一行为由 getIsProduction 与 start 的联动逻辑 保证:start()只有在runInAllEnvironmentstrue、或当前不是生产构建、或显式设置了dangerouslyForceRunInProduction时才会继续执行。其中getIsProduction()通过detectReactBuildType逐个检查已注册渲染器的 bundleType,且刻意不缓存true结果——这是为了应对 Next.js dev overlay 等工具先注册生产版 React、用户的开发版 React 稍后才注册的场景(相关回归测试见 packages/scan/src/core/get-is-production.test.ts)。

如果你希望 react-scan 在生产环境也持续运行,请改用react-scan/all-environments导入路径:

- import { scan } from "react-scan"; + import { scan } from "react-scan/all-environments";

该入口的实现非常精简(见 packages/scan/src/core/all-environments.ts):它只是在浏览器环境下将ReactScanInternals.runInAllEnvironments置为true后转发给内部的scan,从而绕过start()中的生产环境短路检查。该导出在 packages/scan/package.json 中被显式声明为独立的子路径"./all-environments",与./auto./lite./install-hook等入口并列。

需要说明的是:

  • 生产环境运行扫描会带来额外的性能开销,仅应在调试线上问题等特定场景临时使用
  • 它是"主动选择"而非默认行为,不存在误开风险;只有显式使用该导入路径时才会生效。

五、常用 Options 参数参考

模块导入方式下,scan(options)可接受完整的配置对象。以下为 核心 Options 定义 中与日常使用最相关的字段(均带默认值,传入前无需全量填写):

参数类型默认值说明
enabledbooleantrue是否启用扫描。官方推荐的写法是enabled: process.env.NODE_ENV === 'development'
dangerouslyForceRunInProductionbooleanfalse强制在生产环境运行(官方标注 not recommended)
logbooleanfalse将渲染日志输出到控制台;频繁重渲染时会带来明显开销
showToolbarbooleantrue是否显示工具栏;置为trueenabled: false时工具栏仍会显示,但扫描被禁用
animationSpeed"slow" \| "fast" \| "off""fast"高亮动画速度
trackUnnecessaryRendersbooleanfalse追踪"无必要渲染"(组件重渲染但 DOM 子树无变化)并以灰色轮廓标记,会增加额外开销
showFPSbooleantrue工具栏是否显示 FPS 表
showNotificationCountbooleantrue工具栏是否显示卡顿通知数量
allowInIframebooleanfalse是否允许在 iframe 内运行
safeAreanumber \| { top?; right?; bottom?; left? }24工具栏距离视口边缘的像素距离,可应对与 Next.js dev indicator 等浮层重叠的情况
useOffscreenCanvasWorkerbooleantrue是否通过 OffscreenCanvas + Web Worker 渲染轮廓;若 CSP 拒绝worker-src blob:会自动回退到主线程渲染
_debug"verbose" \| falsefalse是否向控制台输出内部错误日志,提交 issue 时很有用

在 setOptions 的选项校验逻辑 中,所有未知或类型错误的参数都会被收集并统一以[React Scan] Invalid options:前缀输出警告,而不会导致崩溃——例如animationSpeed传入"fast"以外的非法值时会回退到默认值并给出提示。此外,enabled等选项会被持久化到localStoragereact-scan-options键),并在下次start()时从本地存储合并回配置(packages/scan/src/core/index.ts#L472-L483),便于在浏览器中即时切换开关。

六、安装与验证

  1. 安装依赖:在 TanStack Start 项目根目录执行pnpm add react-scan(或npm install react-scan/yarn add react-scan)。
  2. 按上文任一方式接入:推荐模块导入方案,将scan调用放入app/routes/__rootuseEffect
  3. 验证生效:启动开发服务器后,页面上应出现 react-scan 的悬浮工具栏;当组件发生重渲染时,对应组件会被高亮轮廓圈出。若控制台出现"[React Scan] Failed to load. Must import React Scan before React runs.",请检查 import 顺序是否满足"先于 React 与 React DOM"的约束。
  4. 生产环境排查:需要线上诊断时,临时将导入路径切换为react-scan/all-environments,排查完成后立即改回。

七、补充说明

  • 本指南针对 TanStack Start;如果你使用 Next.js App Router、Next.js Page Router、Remix、Vite、Parcel、Rsbuild 或 create-react-app,可分别参考 docs/installation 目录下的对应安装文档。
  • react-scan 的实际注入依赖 React DevTools hook 的劫持能力,因此仅适用于浏览器端;所有服务端路径(如react-scan主入口在 package.json exports 中指向rsc-shim的 react-server 条件)都会被 shim 空实现替代,这也是为什么初始化逻辑必须放在客户端组件/客户端入口的原因。
  • 本文中提到的auto.global.jsall-environmentsOptions等均以当前仓库 packages/scan 的源码为准,实际行为以你安装的 react-scan 版本发布物为准。

【免费下载链接】react-scanScan and fix React performance issues项目地址: https://gitcode.com/GitHub_Trending/re/react-scan

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询