React Router v8 于 2026 年 6 月 17 日发布。这个版本没有把升级变成一次大规模重写,而是用较少的破坏性变更,调整了运行时基线:构建产物改为仅支持 ESM,中间件能力也采用默认配置。与此同时,React Router v6 和 Remix v2 已经结束生命周期,仍在使用它们的项目需要把迁移排期提上来。
这次升级改变了什么
ESM-only 不再是可选方向
React Router v8 的构建产物只提供 ESM。这会直接影响以下项目:
- Node.js 服务端渲染入口仍使用
require()或 CommonJS 打包。 package.json没有声明模块类型,构建工具仍按 CommonJS 解析源码。- 测试、脚本或自定义 CLI 直接加载路由模块。
- 依赖链中存在只接受 CommonJS 的旧工具。
前端项目通常已经通过 Vite、Webpack 或其他现代构建工具使用 ESM,因此改动可能集中在入口和构建配置。但 SSR、测试脚本和内部工具往往更容易暴露问题。
可以先检查项目中的 CommonJS 入口:
rg "require\(|module\.exports|__dirname|__filename" \
--glob '*.{js,cjs,mjs,ts,tsx}' \
src server scripts test
这条命令只能帮助定位风险,不能替代构建验证。Node.js 项目可以这样实践,逐步将应用入口切换为 ESM:
{
"name": "router-v8-migration-example",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest run"
},
"dependencies": {
"react": "latest",
"react-dom": "latest",
"react-router": "^8.0.0"
},
"devDependencies": {
"vite": "latest",
"vitest": "latest"
}
}
对应的入口应使用 import 和 export:
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router";
import App from "./App.jsx";
const root = document.getElementById("root");
if (!root) {
throw new Error("Missing #root element");
}
createRoot(root).render(
<BrowserRouter>
<App />
</BrowserRouter>,
);
上面的 package.json 和入口只是一个可改造的最小示例。实际迁移时,版本号应与项目当前的 React、构建工具和运行时兼容范围一起确认。
默认中间件会改变路由请求的组织方式
v8 将 middleware 作为默认能力配置的一部分。它适合承载认证、日志、请求上下文、权限检查和错误处理等横切逻辑,但也意味着团队需要重新审视原来散落在 loader、action、组件 effect 或服务端入口中的代码。
可以按项目约定把中间件拆成独立模块。下面是一个表达迁移思路的最小示例;具体注册函数和参数名称应以项目采用的 v8 API 为准:
// app/middleware/request-context.js
export async function requestContext({ request, next }) {
const requestId = request.headers.get("x-request-id") ?? crypto.randomUUID();
const response = await next({
context: {
requestId,
},
});
response.headers.set("x-request-id", requestId);
return response;
}
路由层可以按同样的边界注册中间件:
// app/routes.js
import { requestContext } from "./middleware/request-context.js";
export const routes = [
{
path: "/",
middleware: [requestContext],
lazy: () => import("./routes/home.js"),
},
];
这段代码的重点不是复制某个项目的最终配置,而是明确三个设计决策:中间件做横切逻辑,路由模块保留业务逻辑,请求上下文通过显式对象传递。不要把所有 loader 和 action 都机械地改成中间件,否则调试链路会变长,错误边界也更难判断。
从 v6 或 Remix v2 迁移时怎么排查
React Router v6 和 Remix v2 已经达到 End of Life。继续运行旧版本并不代表应用会立即失效,但安全修复、缺陷修复和生态兼容性都会逐渐成为风险来源。
可以把迁移拆成几个可验证的步骤:
- 记录当前 React Router、Remix、Node.js、构建工具和测试工具版本。
- 检查 CommonJS 入口、动态加载、SSR 适配器和测试环境。
- 阅读 v8 的迁移指南,确认路由定义、数据 API、中间件和错误处理的差异。
- 先在独立分支升级依赖,运行类型检查、单元测试、构建和端到端测试。
- 对登录、重定向、表单提交、懒加载、404 和 SSR 页面做人工回归。
- 将中间件迁移限制在认证、日志和上下文等稳定边界,不要顺便重写业务路由。
依赖升级可以从以下命令开始:
git switch -c chore/migrate-react-router-v8
npm install react-router@^8
npm run typecheck
npm test
npm run build
如果项目使用 Remix v2,升级不应被理解为简单替换一个 npm 包。Remix 的运行时、文件路由、服务端适配和部署方式都可能影响迁移范围,需要按照当前应用的部署形态逐项验证。
要不要考虑 TanStack Router
部分团队会把这次升级当作重新评估路由方案的机会,例如比较 TanStack Router。这个选择不应只看 API 是否更现代,而要看迁移成本和长期约束:
- 现有路由数量和嵌套路由复杂度。
- 是否依赖 React Router 的数据加载、导航和表单约定。
- 团队对类型安全路由的需求。
- SSR、代码分割、测试工具和部署平台的适配情况。
- 迁移期间是否能接受同时维护两套路由系统。
如果当前应用运行稳定,且 v8 的 ESM 和中间件模型都能被现有工具链接受,升级 React Router 往往比更换路由框架更可控。只有当类型安全、数据路由模型或团队维护成本已经成为持续问题时,才值得把 TanStack Router 作为独立迁移项目评估。
一份务实的升级清单
- [ ] 确认应用的 Node.js 和构建工具支持 ESM。
- [ ] 清理或隔离
require()、module.exports等 CommonJS 入口。 - [ ] 检查 SSR、测试脚本和自定义开发工具的模块加载方式。
- [ ] 按 v8 文档核对路由、数据加载和中间件 API。
- [ ] 为认证、重定向、表单、错误边界和懒加载补充回归测试。
- [ ] 在预发布环境验证构建产物和部署启动命令。
- [ ] 记录是否继续使用 React Router,还是单独评估 TanStack Router。
React Router v8 的“无聊”是一个信号:升级重点从大规模 API 重写转向基础设施一致性。对应用团队来说,真正需要投入时间的地方是模块系统、服务端边界和测试覆盖,而不是盲目重构每一条路由。