如果你正在使用 <RouterProvider>
,请参阅从 RouterProvider 迁移框架。
如果你正在使用 <Routes>
,这里是正确的地方。
React Router Vite 插件为 React Router 添加了框架特性。本指南将帮助你在应用中采用该插件。如果你遇到任何问题,请通过 Twitter 或 Discord 寻求帮助。
Vite 插件添加了
初次设置需要投入最多的工作。但是,一旦完成,你就可以逐步采用新功能,一次迁移一个路由。
使用 Vite 插件,你的项目需要
👉 安装 React Router Vite 插件
npm install -D @react-router/dev
👉 安装运行时适配器
我们假设你正在使用 Node 作为你的运行时。
npm install @react-router/node
👉 将 React 插件替换为 React Router。
-import react from '@vitejs/plugin-react'
+import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
- react()
+ reactRouter()
],
});
👉 创建一个 react-router.config.ts
文件
将以下内容添加到你的项目根目录。在此配置中,你可以告诉 React Router 关于你的项目的信息,例如应用程序目录的位置以及暂时不使用 SSR(服务器端渲染)。
touch react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
appDirectory: "src",
ssr: false,
} satisfies Config;
在典型的 Vite 应用中,index.html
文件是打包的入口点。React Router Vite 插件将入口点移动到 root.tsx
文件,这样你就可以使用 React 渲染应用外壳而不是静态 HTML,并且最终可以升级到服务器渲染(如果你需要)。
👉 将现有的 index.html
移动到 root.tsx
例如,如果你当前的 index.html
看起来像这样
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>My App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
你会将该标记移动到 src/root.tsx
中并删除 index.html
touch src/root.tsx
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "react-router";
export function Layout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
<meta charSet="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>My App</title>
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
export default function Root() {
return <Outlet />;
}
在典型的 Vite 应用中,index.html
文件将 src/main.tsx
指定为客户端入口点。React Router 改为使用名为 src/entry.client.tsx
的文件。
👉 将 src/entry.client.tsx
设置为你的入口点
如果你当前的 src/main.tsx
看起来像这样
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router";
import "./index.css";
import App from "./App";
ReactDOM.createRoot(
document.getElementById("root")!
).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
你会将其重命名为 entry.client.tsx
并将其更改为这样
import React from "react";
import ReactDOM from "react-dom/client";
import { HydratedRouter } from "react-router/dom";
import "./index.css";
ReactDOM.hydrateRoot(
document,
<React.StrictMode>
<HydratedRouter />
</React.StrictMode>
);
hydrateRoot
代替 createRoot
<HydratedRouter>
代替你的 <App/>
组件<App/>
组件。我们将在后续步骤中将其重新加回,但首先我们要确保应用能够使用新的入口点启动。在 root.tsx
和 entry.client.tsx
之间,你可能需要调整一些内容。
通常来说
root.tsx
包含任何渲染相关的内容,例如上下文提供程序 (context providers)、布局 (layouts)、样式等。entry.client.tsx
应该尽可能精简<App/>
组件,我们将在后续步骤中进行。请注意,你的 root.tsx
文件将静态生成并作为应用的入口点提供服务,因此只有该模块需要与服务器渲染兼容。这可能是你遇到麻烦最多的地方。
React Router Vite 插件使用 routes.ts
文件来配置你的路由。现在我们将添加一个简单的全匹配路由 (catchall route) 来启动应用。
👉 设置一个 catchall.tsx
路由
touch src/routes.ts src/catchall.tsx
import {
type RouteConfig,
route,
} from "@react-router/dev/routes";
export default [
// * matches all URLs, the ? makes it optional so it will match / as well
route("*?", "catchall.tsx"),
] satisfies RouteConfig;
👉 渲染一个占位路由
最终我们将用原来的 App
组件替换它,但现在我们只渲染一些简单的内容,以确保应用可以启动。
export default function Component() {
return <div>Hello, world!</div>;
}
查看我们的路由配置指南以了解更多关于 routes.ts
文件信息。
在此阶段,你应该能够启动应用并看到根布局 (root layout)。
👉 添加 dev
脚本并运行应用
"scripts": {
"dev": "react-router dev"
}
在继续之前,请确保此时你的应用能够启动
npm run dev
你可能希望将 .react-router/
添加到你的 .gitignore
文件中,以避免在仓库中跟踪不必要的文件。
.react-router/
你可以查看类型安全以了解如何完整设置和使用自动生成的参数、加载器数据等的类型安全。
为了重新渲染你的应用,我们将更新之前设置的匹配所有 URL 的“全匹配”路由 (catchall route),以便你现有的 <Routes>
有机会渲染。
👉 更新全匹配路由以渲染你的应用
import App from "./App";
export default function Component() {
return <App />;
}
你的应用应该会回到屏幕上并正常工作了!
现在你可以逐步将你的路由迁移到路由模块。
假设现有路由如下
// ...
import About from "./containers/About";
export default function App() {
return (
<Routes>
<Route path="/about" element={<About />} />
</Routes>
);
}
👉 将路由定义添加到 routes.ts
import {
type RouteConfig,
route,
} from "@react-router/dev/routes";
export default [
route("/about", "./pages/about.tsx"),
route("*?", "catchall.tsx"),
] satisfies RouteConfig;
👉 添加路由模块
编辑路由模块以使用路由模块 API
export async function clientLoader() {
// you can now fetch data here
return {
title: "About page",
};
}
export default function Component({ loaderData }) {
return <h1>{loaderData.title}</h1>;
}
请参阅类型安全以设置自动生成的参数、加载器数据等的类型安全。
你迁移的前几个路由是最困难的,因为你通常必须以与以前略有不同的方式访问各种抽象(例如在加载器中而不是从钩子或上下文中访问)。但一旦解决了最棘手的部分,你就可以进入逐步迁移的状态。
如果你想启用服务器渲染和静态预渲染,可以在打包器插件中使用 ssr
和 prerender
选项。对于 SSR,你还需要将服务器构建部署到服务器。
import type { Config } from "@react-router/dev/config";
export default {
ssr: true,
async prerender() {
return ["/", "/about", "/contact"];
},
} satisfies Config;