如果你没有使用 <RouterProvider>
,请查看 从组件路由采用框架。
React Router Vite 插件为 React Router 添加了框架特性。本指南将帮助你在应用中采用此插件。如果遇到任何问题,请在 Twitter 或 Discord 上寻求帮助。
Vite 插件添加了
初始设置需要最多的工作。但是,一旦完成,你可以逐步采用新功能。
要使用 Vite 插件,你的项目需要
React Router Vite 插件渲染它自己的 RouterProvider
,因此你不能在其内部渲染现有的 RouterProvider
。相反,你需要格式化所有路由定义以匹配 路由模块 API。
此步骤将花费最长时间,但是无论是否采用 React Router Vite 插件,这样做都有几个好处
👉 将你的路由定义移至路由模块
按照 路由模块 API,将路由定义的每个部分导出为单独的命名导出。
export async function clientLoader() {
return {
title: "About",
};
}
export default function About() {
let data = useLoaderData();
return <div>{data.title}</div>;
}
// clientAction, ErrorBoundary, etc.
👉 创建转换函数
创建一个辅助函数,将路由模块定义转换为数据路由器期望的格式
function convert(m: any) {
let {
clientLoader,
clientAction,
default: Component,
...rest
} = m;
return {
...rest,
loader: clientLoader,
action: clientAction,
Component,
};
}
👉 懒加载并转换你的路由模块
不要直接导入路由模块,而是懒加载并将它们转换为数据路由器期望的格式。
你的路由定义不仅现在符合路由模块 API,而且你还获得了代码拆分路由的好处。
let router = createBrowserRouter([
// ... other routes
{
path: "about",
- loader: aboutLoader,
- Component: About,
+ lazy: () => import("./routes/about").then(convert),
},
// ... other routes
]);
为应用中的每个路由重复此过程。
一旦所有路由定义都转换为路由模块,你就可以采用 React Router 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 来渲染应用的 shell 而不是静态 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 />;
}
👉 将 RouterProvider
之上的所有内容移动到 root.tsx
任何全局样式、上下文提供程序等都应移动到 root.tsx
,以便它们可以在所有路由之间共享。
例如,如果你的 App.tsx
看起来像这样
import "./index.css";
export default function App() {
return (
<OtherProviders>
<AppLayout>
<RouterProvider router={router} />
</AppLayout>
</OtherProviders>
);
}
你应该将 RouterProvider
之上的所有内容移动到 root.tsx
。
+import "./index.css";
// ... other imports and Layout
export default function Root() {
return (
+ <OtherProviders>
+ <AppLayout>
<Outlet />
+ </AppLayout>
+ </OtherProviders>
);
}
在典型的 Vite 应用中,index.html
文件指向 src/main.tsx
作为客户端入口点。React Router 使用名为 src/entry.client.tsx
的文件代替。
如果 entry.client.tsx
不存在,React Router Vite 插件将使用默认的、隐藏的文件。
👉 将 src/entry.client.tsx
作为你的入口点
如果你的当前 src/main.tsx
看起来像这样
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router";
import App from "./App";
const router = createBrowserRouter([
// ... route definitions
]);
ReactDOM.createRoot(
document.getElementById("root")!
).render(
<React.StrictMode>
<RouterProvider router={router} />;
</React.StrictMode>
);
你应该将其重命名为 entry.client.tsx
并将其更改为这样
import React from "react";
import ReactDOM from "react-dom/client";
import { HydratedRouter } from "react-router/dom";
ReactDOM.hydrateRoot(
document,
<React.StrictMode>
<HydratedRouter />
</React.StrictMode>
);
hydrateRoot
而不是 createRoot
<HydratedRouter>
而不是你的 <App/>
组件<RouterProvider />
。我们将在下一步迁移我们的路由定义。React Router Vite 插件使用 routes.ts
文件来配置你的路由。格式将与你的数据路由器的定义非常相似。
👉 将定义移动到 routes.ts
文件
touch src/routes.ts src/catchall.tsx
将你的路由定义移动到 routes.ts
。请注意,架构并不完全匹配,因此你将收到类型错误;我们将在下一步修复此问题。
+import type { RouteConfig } from "@react-router/dev/routes";
-const router = createBrowserRouter([
+export default [
{
path: "/",
lazy: () => import("./routes/layout").then(convert),
children: [
{
index: true,
lazy: () => import("./routes/home").then(convert),
},
{
path: "about",
lazy: () => import("./routes/about").then(convert),
},
{
path: "todos",
lazy: () => import("./routes/todos").then(convert),
children: [
{
path: ":id",
lazy: () =>
import("./routes/todo").then(convert),
},
],
},
],
},
-]);
+] satisfies RouteConfig;
👉 将 lazy
加载器替换为 file
加载器
export default [
{
path: "/",
- lazy: () => import("./routes/layout").then(convert),
+ file: "./routes/layout.tsx",
children: [
{
index: true,
- lazy: () => import("./routes/home").then(convert),
+ file: "./routes/home.tsx",
},
{
path: "about",
- lazy: () => import("./routes/about").then(convert),
+ file: "./routes/about.tsx",
},
{
path: "todos",
- lazy: () => import("./routes/todos").then(convert),
+ file: "./routes/todos.tsx",
children: [
{
path: ":id",
- lazy: () => import("./routes/todo").then(convert),
+ file: "./routes/todo.tsx",
},
],
},
],
},
] satisfies RouteConfig;
查看我们的路由配置指南,以了解有关 routes.ts
文件和辅助函数的更多信息,以进一步简化路由定义。
此时,你应该已完全迁移到 React Router Vite 插件。继续更新你的 dev
脚本并运行应用,以确保一切正常。
👉 添加 dev
脚本并运行应用
"scripts": {
"dev": "react-router dev"
}
现在,在继续之前,请确保你可以在此时启动你的应用
npm run dev
你可能需要将 .react-router/
添加到你的 .gitignore
文件中,以避免在你的存储库中跟踪不必要的文件。
.react-router/
你可以查看 类型安全,以了解如何完全设置和使用为参数、加载器数据等自动生成的类型安全。
如果你想启用服务端渲染和静态预渲染,你可以使用 bundler 插件中的 ssr
和 prerender
选项来实现。对于 SSR,你还需要将服务器构建部署到服务器。有关更多信息,请参阅 部署。
import type { Config } from "@react-router/dev/config";
export default {
ssr: true,
async prerender() {
return ["/", "/about", "/contact"];
},
} satisfies Config;