快速结论
Strict Mode 改变的是开发环境的检查行为。它会让组件额外重新渲染、让 Effects 额外执行 setup/cleanup、让 ref callback 额外运行,用来暴露不纯渲染和缺少 cleanup 的问题。
它不会直接改变生产环境用户看到的行为。如果生产环境仍然出现重复请求或重复订阅,根因通常是应用代码本身的副作用、依赖数组、缓存策略或请求触发位置,而不是 Strict Mode 本身。
Next.js App Router 默认开启。官方文档说明,自 Next.js 13.5.1 起 App Router 默认启用 Strict Mode;Pages Router 可以通过 next.config.js 的 reactStrictMode 配置启用或关闭。
Strict Mode 会让哪些行为看起来不同
| 你看到的现象 | 开发环境原因 | 应该检查什么 |
|---|---|---|
| 组件函数、console.log 出现两次 | React 额外 re-render 来发现 render 阶段副作用 | 是否在 render 中修改数组、写全局状态、触发请求 |
useEffect 看似执行两次 |
Effect setup 和 cleanup 被额外运行 | 是否缺少清理函数、AbortController、取消订阅 |
| 事件监听、订阅、定时器重复 | 首次挂载流程被模拟清理并再次执行 | cleanup 中是否 remove listener、clearInterval、unsubscribe |
| ref 回调或第三方组件初始化两次 | ref callback 会额外运行以发现缺少清理的问题 | DOM 插件、图表、地图实例是否 destroy/dispose |
Next.js 中如何配置 reactStrictMode
如果你使用 App Router,通常不需要显式写 reactStrictMode: true。如果项目使用 Pages Router,或者你希望团队明确看到配置意图,可以在 next.config.js 中写出。
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
module.exports = nextConfig;
不建议因为“开发环境请求发了两次”就长期关闭 Strict Mode。它暴露的是 effect 不可重入、没有 cleanup、请求缺少取消逻辑等真实风险。
常见错误:在 Effect 里请求但没有清理
下面这类代码在 Strict Mode 开发环境中容易看到重复请求或旧请求晚到后覆盖新状态:
useEffect(() => {
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then((data) => setUser(data));
}, [userId]);
更稳妥的写法是让 Effect 可以被清理,组件卸载或依赖变化时取消旧请求:
useEffect(() => {
const controller = new AbortController();
async function loadUser() {
const res = await fetch(`/api/users/${userId}`, {
signal: controller.signal,
});
const data = await res.json();
setUser(data);
}
loadUser().catch((error) => {
if (error.name !== "AbortError") {
throw error;
}
});
return () => controller.abort();
}, [userId]);
如果使用 React Query、SWR 或 Next.js 服务端数据获取,重复请求问题通常应交给框架缓存、请求去重和服务端渲染边界处理,而不是在客户端 Effect 中硬编码一次性标记。
排查清单
- render 阶段只做纯计算,不发请求、不改全局对象、不直接修改 props 或 state。
useEffect里创建的订阅、定时器、事件监听、第三方实例,都要在 cleanup 中释放。- 网络请求要能取消、忽略过期响应,或使用具备缓存和去重能力的数据层。
- ref callback 中初始化的 DOM 相关资源要能反向清理。
- 只在迁移遗留代码或定位第三方库问题时短期设置
reactStrictMode: false。
FAQ
Strict Mode 会降低线上性能吗?
不会因为额外检查直接降低线上性能。额外 re-render、Effect 重跑和 ref callback 重跑是 development-only 行为。
为什么我关掉 Strict Mode 后问题消失了?
这通常说明 Strict Mode 成功暴露了一个副作用问题。关闭配置只是隐藏症状,缺少 cleanup 或 render 不纯的问题仍然可能在路由切换、快速挂载卸载、并发渲染或真实用户操作中出现。
App Router 和 Pages Router 有什么区别?
App Router 当前默认启用 Strict Mode;Pages Router 项目通常通过 next.config.js 的 reactStrictMode 明确配置。