路由与部署
本教程共 50 篇 · 第 50 篇 · 更新于 2026-07-29 · 约 9 分钟阅读
50. 路由与部署
本节目标:学会用 React Router v7 配置路由,理解数据路由模式,完成项目的构建和部署。
为什么需要路由
单页应用(SPA)只有一个 HTML 文件。URL 变了但页面不刷新。
路由器的职责:监听 URL 变化,渲染对应的组件。
React 官方没有内置路由。社区标准是 React Router。
安装
npm install react-router-dom
创建路由器
React Router v7 推荐用 createBrowserRouter 创建路由器。
// main.jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Root from './routes/root';
import Contact from './routes/contact';
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
children: [
{ path: 'contacts/:contactId', element: <Contact /> },
],
},
]);
ReactDOM.createRoot(document.getElementById('root')).render(
<RouterProvider router={router} />
);
Note
createBrowserRouter使用 HTML5 History API(干净的 URL)。如果要用#哈希路由,换createHashRouter。
嵌套路由
路由可以嵌套。父路由负责布局,子路由负责内容。
const router = createBrowserRouter([
{
path: '/',
element: <Root />, // 布局组件
errorElement: <ErrorPage />, // 错误边界
children: [
{ index: true, element: <Home /> }, // 默认子路由
{ path: 'about', element: <About /> },
{ path: 'contacts/:id', element: <Contact /> },
],
},
]);
父组件里用 <Outlet /> 告诉路由器”子路由渲染在这里”。
import { Outlet, Link } from 'react-router-dom';
function Root() {
return (
<div>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>
<main>
<Outlet /> {/* 子路由渲染位置 */}
</main>
</div>
);
}
路由参数
URL 里的 :xxx 是动态参数。用 useParams 读取。
import { useParams } from 'react-router-dom';
function Contact() {
const { id } = useParams();
return <h1>联系人 #{id}</h1>;
}
编程式导航
除了 <Link>,有时要在代码里跳转。用 useNavigate。
import { useNavigate } from 'react-router-dom';
function LoginButton() {
const navigate = useNavigate();
async function handleLogin() {
await loginUser();
navigate('/dashboard'); // 登录成功后跳转
}
return <button onClick={handleLogin}>登录</button>;
}
数据路由(Loader + Action)
React Router v7 引入了”数据路由”模式。路由配置里直接声明数据加载和提交逻辑。
loader:进入页面时加载数据
// routes/contacts.jsx
import { useLoaderData } from 'react-router-dom';
import { getContacts } from '../api';
export async function loader() {
const contacts = await getContacts();
return { contacts };
}
export default function Contacts() {
const { contacts } = useLoaderData();
return (
<ul>
{contacts.map(c => (
<li key={c.id}>{c.name}</li>
))}
</ul>
);
}
// router.jsx
const router = createBrowserRouter([
{
path: '/contacts',
element: <Contacts />,
loader: loader, // 进入路由前自动调用
},
]);
Tip
loader在组件渲染前执行。数据准备好了才渲染组件,不会出现”先显示空壳再加载”的情况。
action:表单提交
export async function action({ request }) {
const formData = await request.formData();
const name = formData.get('name');
await createContact({ name });
return redirect('/contacts');
}
import { Form } from 'react-router-dom';
function NewContact() {
return (
<Form method="post">
<input name="name" />
<button type="submit">创建</button>
</Form>
);
}
提交表单后 React Router 自动重新调用 loader 刷新数据。不需要手动 useState + useEffect。
错误处理
路由配置里加 errorElement,捕获渲染和数据加载的错误。
import { useRouteError } from 'react-router-dom';
function ErrorPage() {
const error = useRouteError();
return (
<div>
<h1>出错了</h1>
<p>{error.message || error.statusText}</p>
</div>
);
}
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
errorElement: <ErrorPage />, // 所有子路由共享
children: [
// ...
],
},
]);
懒加载路由
配合 React.lazy 和 Suspense,路由级别的代码分割。
import { lazy, Suspense } from 'react';
const HeavyPage = lazy(() => import('./routes/heavy-page'));
const router = createBrowserRouter([
{
path: '/heavy',
element: (
<Suspense fallback={<p>加载中……</p>}>
<HeavyPage />
</Suspense>
),
},
]);
构建
Vite 项目构建命令:
npm run build
构建产物在 dist/ 目录。
构建配置
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist',
sourcemap: false, // 生产环境关闭 sourcemap
rollupOptions: {
output: {
manualChunks: { // 手动分包
react: ['react', 'react-dom'],
router: ['react-router-dom'],
},
},
},
},
});
本地预览构建产物
npm run preview
Warning部署前一定要本地预览。开发环境正常的代码,构建后可能出问题。
部署
静态托管(推荐)
SPA 本质是静态文件。可以部署到任何静态托管服务:
- Vercel:
vercel --prod - Netlify:拖
dist/文件夹上传 - GitHub Pages:把
dist/推到gh-pages分支 - Cloudflare Pages:连接 Git 仓库自动部署
配置 SPA fallback
SPA 的路由是前端处理的。用户直接访问 /about,服务器找不到这个文件。
需要配置:所有路径都回退到 index.html。
Vercel(vercel.json):
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
Netlify(netlify.toml):
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Nginx:
location / {
try_files $uri $uri/ /index.html;
}
环境变量
Vite 用 import.meta.env 读取环境变量。
const apiUrl = import.meta.env.VITE_API_URL;
项目根目录创建 .env.production:
VITE_API_URL=https://api.example.com
Note环境变量必须以
VITE_开头,否则不会暴露给客户端代码。
部署检查清单
上线前逐项检查:
-
npm run build无报错 -
npm run preview本地验证 - 路由直接访问不 404(SPA fallback 配置)
- API 地址指向生产环境
- 环境变量正确配置
- 浏览器控制台无报错
- 移动端适配正常
- 性能评分达标(Lighthouse)
小结
- React Router v7 用
createBrowserRouter创建路由器 - 嵌套路由 +
<Outlet />实现布局复用 loader在进入路由前加载数据action处理表单提交,自动重新验证数据errorElement统一处理错误- Vite 构建产物在
dist/,部署到静态托管服务 - 记得配置 SPA fallback,否则直接访问子路由会 404