从 Remix 升级
本页内容

从 Remix 升级

React Router v7 需要以下最低版本

  • node@20
  • react@18
  • react-dom@18

React Router v7 是 Remix v2 之后的下一个主要版本(有关更多信息,请参阅我们的“React 19 的增量路径”博客文章)。

如果您已启用所有Remix v2 未来特性标志,则从 Remix v2 升级到 React Router v7 主要涉及更新依赖项。

步骤 2-8 中的大多数步骤可以使用社区成员 James Restall 创建的 codemod 自动更新。

1. 采用未来特性标志

👉 采用未来特性标志

在您的 Remix v2 应用程序中采用所有现有的未来特性标志

2. 更新依赖项

过去通过运行时特定软件包(@remix-run/node@remix-run/cloudflare 等)重新导出的“共享” API 大部分已在 v7 中折叠到 react-router 中。因此,您将直接从 react-router 导入这些 API,而不是从 @react-router/node@react-router/cloudflare 导入。

-import { redirect } from "@remix-run/node";
+import { redirect } from "react-router";

在 v7 中,您应该从运行时特定软件包导入的唯一 API 是特定于该运行时的 API,例如 Node 的 createFileSessionStorage 和 Cloudflare 的 createWorkersKVSessionStorage

👉 运行 codemod(自动)

您可以使用以下 codemod 自动更新您的软件包和导入。此 codemod 会更新您的所有软件包和导入。请务必在运行 codemod 之前提交任何待处理的更改,以防您需要还原。

npx codemod remix/2/react-router/upgrade

👉 安装新的依赖项

在 codemod 更新您的依赖项后,您需要安装依赖项以删除 Remix 软件包并添加新的 React Router 软件包。

npm install

👉 更新您的依赖项(手动)

如果您不想使用 codemod,您可以手动更新您的依赖项。

展开以查看按字母顺序排列的软件包名称更改表
Remix v2 软件包 React Router v7 软件包
@remix-run/architect ➡️ @react-router/architect
@remix-run/cloudflare ➡️ @react-router/cloudflare
@remix-run/dev ➡️ @react-router/dev
@remix-run/express ➡️ @react-router/express
@remix-run/fs-routes ➡️ @react-router/fs-routes
@remix-run/node ➡️ @react-router/node
@remix-run/react ➡️ react-router
@remix-run/route-config ➡️ @react-router/dev
@remix-run/routes-option-adapter ➡️ @react-router/remix-routes-option-adapter
@remix-run/serve ➡️ @react-router/serve
@remix-run/server-runtime ➡️ react-router
@remix-run/testing ➡️ react-router

3. 更改 package.json 中的 scripts

如果您使用了 codemod,则可以跳过此步骤,因为它已自动完成。

👉 更新 package.json 中的脚本

脚本 Remix v2 React Router v7
dev remix vite:dev ➡️ react-router dev
build remix vite:build ➡️ react-router build
start remix-serve build/server/index.js ➡️ react-router-serve build/server/index.js
typecheck tsc ➡️ react-router typegen && tsc

4. 添加 routes.ts 文件

如果您使用了 codemod并且 Remix v2 v3_routeConfig 标志,则可以跳过此步骤,因为它已自动完成。

在 React Router v7 中,您可以使用 app/routes.ts 文件定义路由。查看路由文档以获取更多信息。

👉 更新依赖项(如果使用 Remix v2 v3_routeConfig 标志)

// app/routes.ts
-import { type RouteConfig } from "@remix-run/route-config";
-import { flatRoutes } from "@remix-run/fs-routes";
-import { remixRoutesOptionAdapter } from "@remix-run/routes-option-adapter";
+import { type RouteConfig } from "@react-router/dev/routes";
+import { flatRoutes } from "@react-router/fs-routes";
+import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";

export default [
  // however your routes are defined
] satisfies RouteConfig;

👉 添加 routes.ts 文件(如果使用 Remix v2 v3_routeConfig 标志)

touch app/routes.ts

为了向后兼容,以及对于喜欢基于文件的约定的人,您可以选择加入与 Remix v2 中使用相同的“扁平路由”约定,通过新的 @react-router/fs-routes 软件包

import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export default flatRoutes() satisfies RouteConfig;

或者,如果您使用 routes 选项来定义基于配置的路由

import { type RouteConfig } from "@react-router/dev/routes";
import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";

export default remixRoutesOptionAdapter((defineRoutes) => {
  return defineRoutes((route) => {
    route("/", "home/route.tsx", { index: true });
    route("about", "about/route.tsx");
    route("", "concerts/layout.tsx", () => {
      route("trending", "concerts/trending.tsx");
      route(":city", "concerts/city.tsx");
    });
  });
}) satisfies RouteConfig;

如果您在 vite.config.ts 中使用了 routes 选项,请务必将其删除。

export default defineConfig({
  plugins: [
    remix({
      ssr: true,
-     ignoredRouteFiles: ['**/*'],
-     routes(defineRoutes) {
-       return defineRoutes((route) => {
-         route("/somewhere/cool/*", "catchall.tsx");
-       });
-     },
    })
    tsconfigPaths(),
  ],
});

5. 添加 React Router 配置

👉 将 react-router.config.ts 添加到您的项目

以前传递给 vite.config.tsremix 插件的配置现在从 react-router.config.ts 导出。

注意:此时,您应该删除在步骤 1 中添加的 v3 未来特性标志。

touch react-router.config.ts
// vite.config.ts
export default defineConfig({
  plugins: [
-   remix({
-     ssr: true,
-     future: {/* all the v3 flags */}
-   }),
+   remix(),
    tsconfigPaths(),
  ],
});

// react-router.config.ts
+import type { Config } from "@react-router/dev/config";
+export default {
+  ssr: true,
+} satisfies Config;

6. 将 React Router 插件添加到 vite.config

如果您使用了 codemod,则可以跳过此步骤,因为它已自动完成。

👉 将 reactRouter 插件添加到 vite.config

更改 vite.config.ts 以导入和使用来自 @react-router/dev/vite 的新 reactRouter 插件

-import { vitePlugin as remix } from "@remix-run/dev";
+import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [
-   remix(),
+   reactRouter(),
    tsconfigPaths(),
  ],
});

7. 启用类型安全

如果您未使用 TypeScript,则可以跳过此步骤。

React Router 会自动为您的路由模块生成类型,并将其放入应用程序根目录的 .react-router/ 目录中。此目录完全由 React Router 管理,应添加到 .gitignore 中。了解有关新的类型安全功能的更多信息。

👉 将 .react-router/ 添加到 .gitignore

.react-router/

👉 更新 tsconfig.json

更新 tsconfig.json 中的 types 字段以包含

  • .react-router/types/**/* 路径在 include 字段中
  • types 字段中相应的 @react-router/* 软件包
  • 用于简化相对导入的 rootDirs
{
  "include": [
    /* ... */
+   ".react-router/types/**/*"
  ],
  "compilerOptions": {
-   "types": ["@remix-run/node", "vite/client"],
+   "types": ["@react-router/node", "vite/client"],
    /* ... */
+   "rootDirs": [".", "./.react-router/types"]
  }
}

8. 重命名入口文件中的组件

如果您使用了 codemod,则可以跳过此步骤,因为它已自动完成。

如果您的应用程序中有一个 entry.server.tsx 和/或 entry.client.tsx 文件,您将需要更新这些文件中的主要组件

-import { RemixServer } from "@remix-run/react";
+import { ServerRouter } from "react-router";

-<RemixServer context={remixContext} url={request.url} />,
+<ServerRouter context={remixContext} url={request.url} />,
-import { RemixBrowser } from "@remix-run/react";
+import { HydratedRouter } from "react-router/dom";

hydrateRoot(
  document,
  <StrictMode>
-   <RemixBrowser />
+   <HydratedRouter />
  </StrictMode>,
);

9. 更新 AppLoadContext 的类型

如果您使用 remix-serve,则可以跳过此步骤。这仅适用于您在 Remix v2 中使用自定义服务器的情况。

由于 React Router 既可以用作 React 框架又可以用作独立的路由库,因此 LoaderFunctionArgsActionFunctionArgscontext 参数现在是可选的,默认类型为 any。您可以为您的加载上下文注册类型,以便为您的加载器和操作获得类型安全。

👉 为您的加载上下文注册类型

在您迁移到新的 Route.LoaderArgsRoute.ActionArgs 类型之前,您可以暂时使用您的加载上下文类型来扩充 LoaderFunctionArgsActionFunctionArgs,以简化迁移。

declare module "react-router" {
  // Your AppLoadContext used in v2
  interface AppLoadContext {
    whatever: string;
  }

  // TODO: remove this once we've migrated to `Route.LoaderArgs` instead for our loaders
  interface LoaderFunctionArgs {
    context: AppLoadContext;
  }

  // TODO: remove this once we've migrated to `Route.ActionArgs` instead for our actions
  interface ActionFunctionArgs {
    context: AppLoadContext;
  }
}

export {}; // necessary for TS to treat this as a module

使用 declare module 注册类型是一种标准的 TypeScript 技术,称为 模块扩充。您可以在 tsconfig.jsoninclude 字段涵盖的任何 TypeScript 文件中执行此操作,但我们建议在您的 app 目录中使用专用的 env.ts

👉 使用新类型

一旦您采用了新的类型生成,您就可以删除 LoaderFunctionArgs/ActionFunctionArgs 扩充,并使用来自Route.LoaderArgsRoute.ActionArgscontext 参数。

declare module "react-router" {
  // Your AppLoadContext used in v2
  interface AppLoadContext {
    whatever: string;
  }
}

export {}; // necessary for TS to treat this as a module
import type { Route } from "./+types/my-route";

export function loader({ context }: Route.LoaderArgs) {}
// { whatever: string }  ^^^^^^^

export function action({ context }: Route.ActionArgs) {}
// { whatever: string }  ^^^^^^^

恭喜!您现在正在使用 React Router v7。继续运行您的应用程序以确保一切正常运行。

文档和示例 CC 4.0