React Router 路由

到目前为止我们写的都是单页应用,URL 永远是 /。真实项目需要多个页面:首页、关于、用户列表、文章详情。React 本身不带路由(它只是个 UI 库),但社区有事实标准库 React Router。这一章我们学会用它做多页应用。

1. 为什么需要路由

传统的多页网站,点链接会整页刷新——服务器返回新 HTML,浏览器重新加载所有资源,体验差。单页应用(SPA)的做法是:整个应用只有一个 HTML,点链接时用 JS 切换显示的内容,不刷新页面,体验丝滑。但要解决两个问题:URL 要变(方便分享、收藏)、浏览器前进后退要工作——这就是路由干的事。

2. 安装

# React Router v6(目前主流版本)
npm install react-router-dom

# 它会作为 dependencies 装进 package.json
# react-router-dom = react-router 核心 + DOM 相关 API(浏览器用)

本系列所有代码基于 React Router v6(目前主流)。v5 的 API 差异较大,看老教程时注意区分。

3. 基础路由

三个核心组件:BrowserRouter(包裹应用,启用路由)、Routes(路由容器)、Route(单条路由规则)。

import { BrowserRouter, Routes, Route } from "react-router-dom";

function App() {
  return (
    // BrowserRouter 包在最外层,启用路由功能
    <BrowserRouter>
      {/* Routes 是所有路由的容器 */}
      <Routes>
        {/* path 是 URL 路径,element 是要渲染的组件 */}
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  );
}

function Home() { return <h1>首页</h1>; }
function About() { return <h1>关于我们</h1>; }
function Contact() { return <h1>联系我们</h1>; }
// 访问 /  → Home
// 访问 /about → About
// 访问 /contact → Contact

path 是 URL 路径,element 是路径匹配时渲染的组件。访问不同 URL,React Router 自动切换显示对应组件——整个过程不刷新页面。

4. 用 Link 做导航

页面之间的跳转用 Link 组件,它会渲染成 <a> 标签,但点击时拦截默认跳转,改成 JS 切换路由:

import { Link, NavLink } from "react-router-dom";

function NavBar() {
  return (
    <nav>
      {/* Link:渲染成 <a>,但不会真的刷新页面(SPA 核心) */}
      <Link to="/">首页</Link>
      <Link to="/about">关于</Link>

      {/* NavLink:当前页对应的会自动加 active class */}
      <NavLink to="/about" className="nav-link">
        关于(高亮当前)
      </NavLink>
    </nav>
  );
}

// ❌ 不要用 <a href="/about">:会整页刷新,SPA 体验全毁

大忌:用原生 <a href="/about"> 会整页刷新,SPA 体验全毁。永远用 <Link to="...">。当前页对应的链接需要高亮时,用 NavLink——它会自动给激活的链接加 active class。

5. 动态路由参数

真实场景里,URL 经常带动态参数:用户详情 /users/42、文章 /posts/abc-123。React Router 用 :参数名 语法声明:

import { useParams } from "react-router-dom";

// 路由路径里的 :id 是"动态参数"
<Route path="/users/:id" element={<UserDetail />} />

function UserDetail() {
  // useParams 读取 URL 里的参数
  const { id } = useParams();
  return <h1>用户 ID:{id}</h1>;
}

// 访问 /users/42 → 显示"用户 ID:42"
// 访问 /users/abc → 显示"用户 ID:abc"

这是后端风格的路由,语义清晰、SEO 友好。useParams 是个 Hook,在任何子组件里都能调,拿到当前 URL 的参数。

6. 编程式导航

有时不能等用户点链接——比如登录成功后自动跳转、表单提交后跳到结果页。这时用 useNavigate Hook:

import { useNavigate } from "react-router-dom";

function LoginForm() {
  const navigate = useNavigate();

  const handleLogin = async () => {
    // ... 登录逻辑
    const ok = await login();
    if (ok) {
      navigate("/dashboard");       // 编程式跳转
      // navigate(-1)  → 后退一页(等价于浏览器后退)
      // navigate(1)   → 前进一页
      // navigate("/p", { replace: true })  → 替换历史(不留下当前页)
    }
  };

  return <button onClick={handleLogin}>登录</button>;
}

navigate(path) 跳到指定路径,navigate(-1) 后退一页,navigate(1) 前进一页。配合 replace: true 可以"替换"历史(用户后退不会回到当前页)。

7. 嵌套路由

后台管理这类应用,有共享的布局(侧边栏、顶栏),只有内容区切换。React Router 用嵌套路由 + Outlet 优雅处理:

// 嵌套路由:Layout 共享侧边栏,内容区切换
function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* 父路由用 Outlet 标记子路由渲染位置 */}
        <Route path="/dashboard" element={<DashboardLayout />}>
          {/* 子路由 path 自动拼成 /dashboard/users */}
          <Route index element={<Overview />} />
          <Route path="users" element={<Users />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

function DashboardLayout() {
  return (
    <div className="layout">
      <Sidebar />
      <main>
        {/* Outlet:子路由渲染在这里 */}
        <Outlet />
      </main>
    </div>
  );
}

父路由的组件里放一个 <Outlet />,标记"子路由渲染在这里"。访问 /dashboard/users 时,DashboardLayoutUsers 会同时渲染——布局共享,内容切换。index 路由表示父路径本身(/dashboard)显示的默认内容。

8. 404 页面

用户可能输入不存在的 URL,你需要一个"兜底"路由显示 404 页面:

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        {/* 兜底路由:放最后,匹配所有未命中的路径 */}
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

function NotFound() {
  return (
    <div>
      <h1>404 - 页面不存在</h1>
      <Link to="/">返回首页</Link>
    </div>
  );
}

path="*" 是通配符,匹配所有未命中的路径。必须放在 Routes 的最后,否则它会"截胡"后面的路由。

9. 查询参数

除了 path 参数,URL 还有查询参数(?page=2&size=10)。用 useSearchParams 读取:

小结

React Router 让单页应用拥有多页体验。核心组件:BrowserRouter 包外层、Routes 装路由、Route 定义规则、Link 做导航。动态参数用 :id + useParams,编程式跳转用 useNavigate,共享布局用嵌套路由 + Outlet。下一篇看 React 组件"生命周期"——useEffect

← 上一篇 受控组件与表单

下一篇 生命周期与 useEffect

✈️💬