從 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

為了向後相容性,以及為了偏好基於檔案慣例的人員,您可以透過新的 @react-router/fs-routes 套件選擇加入您在 Remix v2 中使用的相同「扁平路由」慣例

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 檔案中執行此操作,但我們建議在您的應用程式目錄中使用專用的 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。繼續執行您的應用程式,以確保一切都如預期般運作。