React Router v7 需要以下最低版本
node@20
react@18
react-dom@18
React Router v7 是 Remix v2 之后的下一个主要版本(更多信息请参阅我们的“通往 React 19 的增量路径”博文)。
如果您已启用所有 Remix v2 future flags,从 Remix v2 升级到 React Router v7 主要涉及更新依赖项。
第 2-8 步中的大部分内容都可以使用社区成员 James Restall 创建的 codemod 自动更新。
👉 采用 future flags
在您的 Remix v2 应用程序中采用所有现有的 future flags。
大多数过去通过特定运行时包(@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 |
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 |
routes.ts
文件如果您使用了 codemod *并且* Remix v2 的 v3_routeConfig
标志,可以跳过此步骤,因为它已自动完成。
在 React Router v7 中,您使用 app/routes.ts
文件来定义路由。查看路由文档以获取更多信息。
👉 更新依赖项(如果使用 Remix v2 v3_routeConfig
标志)
-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
为了向后兼容,有几种方法可以采用 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;
如果您正在使用来自 Remix v1 的“嵌套”约定(通过 @remix-run/v1-route-convention
包),您也可以结合 @react-router/remix-routes-option-adapter
继续使用它
import { type RouteConfig } from "@react-router/dev/routes";
import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";
import { createRoutesFromFolders } from "@remix-run/v1-route-convention";
export default remixRoutesOptionAdapter(
createRoutesFromFolders,
) satisfies RouteConfig;
如果您正在使用 routes
选项来定义基于配置的路由,您可以通过 @react-router/remix-routes-option-adapter
保留该配置
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(),
],
});
👉 将 react-router.config.ts
添加到您的项目中
之前传递给 vite.config.ts
中 remix
插件的配置现在从 react-router.config.ts
中导出。
注意:此时您应该移除在第 1 步中添加的 v3 future flags。
touch react-router.config.ts
export default defineConfig({
plugins: [
- remix({
- ssr: true,
- future: {/* all the v3 flags */}
- }),
+ reactRouter(),
tsconfigPaths(),
],
});
+import type { Config } from "@react-router/dev/config";
+export default {
+ ssr: true,
+} satisfies Config;
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(),
],
});
如果您不使用 TypeScript,可以跳过此步骤。
React Router 会自动为您的路由模块生成类型到您应用根目录下的 .react-router/
目录中。该目录由 React Router 完全管理,并应被 gitignore。了解更多关于新的类型安全特性。
👉 将 .react-router/
添加到 .gitignore
.react-router/
👉 更新 tsconfig.json
更新您 tsconfig.json
中的 types
字段以包含
include
字段中包含 .react-router/types/**/*
路径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"]
}
}
如果您使用了 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>,
);
AppLoadContext
的类型如果您正在使用 remix-serve
,可以跳过此步骤。这仅适用于您在 Remix v2 中使用自定义服务器的情况。
由于 React Router 既可以作为 React 框架使用,也可以作为独立的路由库使用,LoaderFunctionArgs
和 ActionFunctionArgs
的 context
参数现在是可选的,并且默认类型为 any
。您可以为您的加载上下文注册类型,以获得 loaders 和 actions 的类型安全。
👉 为您的加载上下文注册类型
在您迁移到新的 Route.LoaderArgs
和 Route.ActionArgs
类型之前,您可以暂时使用您的加载上下文类型来增强 LoaderFunctionArgs
和 ActionFunctionArgs
,以简化迁移过程。
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.json
的 include
字段覆盖的任何 TypeScript 文件中执行此操作,但我们建议在您的应用目录中创建一个专用的 env.ts
文件。
👉 使用新的类型
一旦您采用新的类型生成,您可以移除 LoaderFunctionArgs
/ActionFunctionArgs
的增强,并改用来自 Route.LoaderArgs
和 Route.ActionArgs
的 context
参数。
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。现在去运行您的应用程序,确保一切都按预期工作。