React Scan 在 Next.js Page Router 中的接入指南:script 标签与模块导入两种方式详解
2026/9/13 19:47:33 网站建设 项目流程

React Scan 在 Next.js Page Router 中的接入指南:script 标签与模块导入两种方式详解

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

本篇技术指南以 React Scan 官方文档中的 Next.js Page Router 安装指南 为主体,详细讲解如何在基于Pages Routerpages/目录)的 Next.js 应用中接入 React Scan,以扫描并定位 React 组件的重复渲染与性能瓶颈。读完本文,你将掌握两种接入方式(pages/_document中的 CDN script 标签、pages/_app中的模块导入)、如何在生产环境强制启用扫描(react-scan/all-environments),并能理解其"仅开发环境生效"的底层判定逻辑,从而避免踩坑。

适用前提

React Scan(react-scan包,当前仓库版本为0.5.7,见 packages/scan/package.json)通过 React DevTools Hook(基于bippy)在运行时自动检测 React 渲染行为,无需改动业务组件代码。其 peerDependencies 声明支持react/react-dom^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0,因此 Pages Router 下无论是 Next.js 12、13、14 还是 15 的项目,只要满足上述 React 版本要求即可接入。

本文针对的是Page Router(pages/目录);如果你的项目使用 App Router(app/目录),请参考 App Router 安装指南。

方式一:通过 CDN script 标签接入

这是最快的接入方式——不需要安装任何 npm 包,只需要在pages/_document中添加一个 script 标签。

修改pages/_document

打开(或创建)pages/_document,将 React Scan 的auto.global.js<script>标签引入到<Head>中:

// pages/_document import { Html, Head, Main, NextScript } from "next/document"; export default function Document() { return ( <Html lang="en"> <Head> <script src="https://unpkg.com/react-scan/dist/auto.global.js" /> {/* 其余脚本放在下面 */} </Head> <body> <Main /> <NextScript /> </body> </Html> ); }

之所以放在pages/_document<Head>里,是因为_document只在服务端渲染时执行,最终生成的auto.global.js会随首屏 HTML 一并下发,从而保证脚本在应用 JavaScript 执行前就绪。

可用的 CDN 地址

官方 CDN 指南 提供以下两个等价地址,任选其一:

<!-- JSDelivr --> <script src="https://cdn.jsdelivr.net/npm/react-scan/dist/auto.global.js"></script> <!-- UNPKG --> <script src="https://unpkg.com/react-scan/dist/auto.global.js"></script>

auto.global.js 做了什么

auto.global.js对应源码 packages/scan/src/auto.ts:

import './polyfills'; // Prioritize bippy side-effect import 'bippy'; import { IS_CLIENT } from '~web/utils/constants'; import { scan } from './index'; if (IS_CLIENT) { scan(); window.reactScan = scan; }

即在客户端环境下自动调用scan()完成初始化,无需任何手动配置;同时把scan挂到window.reactScan上,方便在浏览器控制台里随时手动开关扫描。这正是 script 标签方式"开箱即用"的原因。

方式二:通过模块导入接入

如果你更希望把依赖纳入 npm 管理(而非 CDN),可以采用模块导入方式。首先安装:

npm install react-scan # 或 pnpm add react-scan

然后在pages/_app中完成初始化。

修改pages/_app

// pages/_app // react-scan 必须是文件中最顶部的导入 import { scan } from "react-scan"; import { useEffect } from "react"; export default function App({ Component, pageProps }) { useEffect(() => { // 确保在 React 水合(hydration)完成之后再启动扫描 scan({ enabled: true, }); }, []); return <Component {...pageProps} />; }

这里有两点必须注意

  1. import { scan } from "react-scan"必须是文件中最顶部的导入。React Scan 依赖bippy的副作用来安装 React 内部的 hook,只有先于 React 相关模块执行,才能在 React 渲染管线中挂上探针。若导入顺序不对,控制台会在 5 秒后报出[React Scan] Failed to load. Must import React Scan before React runs.(该逻辑见 packages/scan/src/core/index.ts)。
  2. scan()必须放在useEffect中、即水合完成之后调用。在渲染阶段调用会干扰 React 的正常提交流程;放在useEffect空依赖数组中可保证只在客户端、只在首次挂载后执行一次。

在生产环境也启用扫描:react-scan/all-environments

默认情况下,React Scan只在开发环境运行——这是由 packages/scan/src/core/index.ts 中start()的逻辑决定的:当检测到当前 React 为生产构建(getIsProduction()返回true)且未开启dangerouslyForceRunInProduction时,直接跳过初始化。

如果你需要在生产环境(例如线上复现性能问题)也启用扫描,把导入路径换成react-scan/all-environments

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

该导出子路径在 packages/scan/package.json 中声明,其实现位于 packages/scan/src/core/all-environments.ts:

import { ReactScanInternals, scan as innerScan } from '.'; export const scan = /*#__PURE__*/ (...params: Parameters<typeof innerScan>) => { if (typeof window !== 'undefined') { ReactScanInternals.runInAllEnvironments = true; innerScan(...params); } };

原理很简单:调用前先把内部标志ReactScanInternals.runInAllEnvironments置为truestart()中的环境检查(见 packages/scan/src/core/index.ts)便会跳过生产环境拦截,从而实现任意环境(开发、生产、iframe)都能扫描。script 标签方式没有等价的"生产开关",如需生产扫描请使用模块导入方式。

源码级原理解析:scan() 的完整调用链

模块导入方式触发的核心调用链为scan(options)setOptions(options)+start(),均位于 packages/scan/src/core/index.ts:

export const scan = (options: Options = {}) => { setOptions(options); const isInIframe = Store.isInIframe.value; if ( isInIframe && !ReactScanInternals.options.value.allowInIframe && !ReactScanInternals.runInAllEnvironments ) { return; } if (options.enabled === false && options.showToolbar !== true) { return; } start(); };

可以看到三个关键判定:

  • iframe 保护:默认allowInIframefalse,在 iframe 内默认不启动(除非显式开启或使用all-environments);
  • 显式关闭enabled: false且未要求显示工具栏时直接返回;
  • 环境门槛start()内通过getIsProduction()判断当前 React 是否为生产构建(见 packages/scan/src/core/index.ts)。

值得注意的一个细节:getIsProduction()对"检测到开发构建"的结果做了永久缓存,但故意不缓存"生产构建"的结果。其测试用例 packages/scan/src/core/get-is-production.test.ts 注释了原因——Next.js 的 dev overlay 会先注册一个生产版 React 渲染器,随后用户的开发版 React 才在下一 tick 注册;若把首次的true缓存下来,就会把 dev 环境误判为生产而锁死扫描。这个边界情况正是 Next.js 项目中最容易踩到的坑,也是官方通过回归测试专门防护的。

常用配置项

pages/_app中调用scan()时传入的Options定义于 packages/scan/src/core/index.ts,常用项及默认值如下:

配置项类型默认值说明
enabledbooleantrue是否启用扫描,推荐写成process.env.NODE_ENV === 'development'
showToolbarbooleantrue是否显示右下角工具栏;设为true时即使enabled: false工具栏仍显示,但扫描关闭
animationSpeed"slow" \| "fast" \| "off""fast"渲染高亮动画速度
logbooleanfalse将渲染日志打印到控制台,注意频繁渲染时会带来明显开销
trackUnnecessaryRendersbooleanfalse追踪"无效渲染"(组件重渲染但 DOM 子树无变化)并以灰色轮廓标记,会带来额外开销
showFPSbooleantrue工具栏中是否显示 FPS 计数器
showNotificationCountbooleantrue工具栏中是否显示性能提醒数量
allowInIframebooleanfalse是否允许在 iframe 内运行
safeAreanumber \| { top?, right?, bottom?, left? }24工具栏距视口边缘的像素距离,可避免与 Next.js dev indicator 等覆盖层重叠
dangerouslyForceRunInProductionbooleanfalse强制在生产环境运行(不推荐,优先使用all-environments导入)

一个推荐的开发环境写法:

import { scan } from "react-scan"; import { useEffect } from "react"; export default function App({ Component, pageProps }) { useEffect(() => { scan({ enabled: process.env.NODE_ENV === "development", trackUnnecessaryRenders: true, }); }, []); return <Component {...pageProps} />; }

注意:setOptions会通过validateOptions校验传入项(见 packages/scan/src/core/index.ts),非法值(如animationSpeed传了"normal")不会生效,并会在控制台输出[React Scan] Invalid options警告;enabled等布尔选项同时会被持久化到localStorage(键名react-scan-options),因此刷新页面后开关状态会保留。

常见问题排查

  • 控制台出现[React Scan] Failed to load. Must import React Scan before React runs.:说明react-scan的导入不在最顶部,或 script 标签加载晚于 React 执行。请调整导入顺序,或改用pages/_document的 CDN 方式。
  • 生产环境没有高亮效果:这是预期行为——默认仅开发环境生效。需要生产扫描请改用react-scan/all-environments
  • 页面在 iframe 中无扫描效果:默认allowInIframe: false,若目标场景在 iframe 中,请显式开启该选项。
  • 工具栏与 Next.js 自带开发指示器重叠:使用safeArea配置项调整工具栏的安全间距。

小结

在 Next.js Page Router 项目中接入 React Scan 共有两条路径:CDN script 标签(零依赖、修改pages/_document即可)与模块导入(纳入 npm 管理、修改pages/_app)。两者都遵循同一底层实现——通过bippy安装 React hook、scan()完成初始化的调用链,并默认只在开发环境生效;如需生产环境扫描,模块导入方式可无缝切换到react-scan/all-environments。接入之后,组件每次渲染都会以高亮轮廓的形式呈现在页面上,配合工具栏的 FPS 与渲染统计,即可快速定位"谁在渲染、为什么渲染"。

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

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

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

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

立即咨询