首页 / React 19 入门教程 / 路由与部署

React 19 入门教程

路由与部署

本教程共 50 篇 · 第 50 篇 · 更新于 2026-07-29 · 约 9 分钟阅读

ReactReact Router路由部署Vite构建

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.lazySuspense,路由级别的代码分割。

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 本质是静态文件。可以部署到任何静态托管服务:

  • Vercelvercel --prod
  • Netlify:拖 dist/ 文件夹上传
  • GitHub Pages:把 dist/ 推到 gh-pages 分支
  • Cloudflare Pages:连接 Git 仓库自动部署

配置 SPA fallback

SPA 的路由是前端处理的。用户直接访问 /about,服务器找不到这个文件。

需要配置:所有路径都回退到 index.html

Vercelvercel.json):

{
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}

Netlifynetlify.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
上一篇
常见反模式与坑
下一篇
已经是最后一篇啦