React Router v8:一次刻意保持“无聊”的升级,重点是 ESM 与默认中间件

2026-08-19 40 预计阅读时间: 1 分钟
来源: infoq.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:9 分钟

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"
  }
}

对应的入口应使用 importexport

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。继续运行旧版本并不代表应用会立即失效,但安全修复、缺陷修复和生态兼容性都会逐渐成为风险来源。

可以把迁移拆成几个可验证的步骤:

  1. 记录当前 React Router、Remix、Node.js、构建工具和测试工具版本。
  2. 检查 CommonJS 入口、动态加载、SSR 适配器和测试环境。
  3. 阅读 v8 的迁移指南,确认路由定义、数据 API、中间件和错误处理的差异。
  4. 先在独立分支升级依赖,运行类型检查、单元测试、构建和端到端测试。
  5. 对登录、重定向、表单提交、懒加载、404 和 SSR 页面做人工回归。
  6. 将中间件迁移限制在认证、日志和上下文等稳定边界,不要顺便重写业务路由。

依赖升级可以从以下命令开始:

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 重写转向基础设施一致性。对应用团队来说,真正需要投入时间的地方是模块系统、服务端边界和测试覆盖,而不是盲目重构每一条路由。


相关推荐