React Router 完全指南:从入门到实战,一文搞懂路由核心技巧

0 阅读7分钟

React Router 完全指南:从入门到实战,一文搞懂路由核心技巧

你是否还在为路由配置发愁?是否分不清 BrowserRouterHashRouter 的区别?本文将带你从零开始,系统掌握 React Router 的核心用法,包括懒加载、动态路由、嵌套路由、鉴权拦截等实战必备技能,并附上 Vue 与 React 路由的对比思考。


前言:为什么需要前端路由?

在传统的 Web 开发中,每次页面跳转都需要向服务器请求新的 HTML,导致页面刷新、体验卡顿。而随着 Ajax 技术的普及,我们可以在不刷新页面的情况下与服务器交换数据并局部更新网页。

这催生了 SPA(Single Page Application,单页应用) 的诞生——整个网站只有一个 HTML 页面,所有页面切换由前端路由控制,内容通过 JavaScript 动态渲染,实现无刷新切换。

SPA 的优点:体验流畅,类似原生 App。
SPA 的缺点:SEO 不友好(搜索引擎难以抓取动态内容),需配合 SSR(服务端渲染)解决。

关键关系

  • Ajax 是手段:负责异步获取数据,局部更新。
  • SPA 是终极形态:依赖前端路由 + Ajax 实现 URL 变化但不刷新页面。

一、路由选型:BrowserRouter vs HashRouter

React Router 提供了两种路由模式,区别如下:

模式创建方式URL 示例特点
BrowserRoutercreateBrowserRouterhttp://localhost/home依赖 History API,URL 干净,符合 RESTful 风格;部署时需要服务端配置回退到 index.html
HashRoutercreateHashRouterhttp://localhost/#/home利用 hash 路由,无需服务端支持,适合静态站点

建议:大多数项目优先使用 BrowserRouter,除非你部署在纯静态服务器上(如 GitHub Pages)。


二、前置准备:项目根路由结构

main.jsx(或 index.js)中,先用 <BrowserRouter><HashRouter> 包裹整个应用:

// main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

三、核心路由技巧(实战篇)

1. 懒加载(按需加载)—— 提升首屏性能

场景:大型项目首页加载太慢,需要代码分割,只有访问该页面时才加载对应的 JS 文件。

实现方式

  • 使用 React.lazy() 定义组件,实现异步加载。
  • 使用 <Suspense> 包裹路由组件,并指定 fallback 作为加载中的占位 UI(如骨架屏、Loading 图标)。
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

function App() {
  return (
    <Suspense fallback={<div>页面加载中...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  );
}

注意lazy 组件必须放在 Suspense 内部,否则会报错。


2. 动态路由 —— 处理参数(如 /user/123

场景:详情页、个人主页等,URL 中携带 ID 或唯一标识。

实现方式

  • 路径中用 :参数名 占位。
  • 组件内用 useParams() 获取参数。
// 路由配置
<Route path="/user/:id" element={<UserDetail />} />

// UserDetail.jsx
import { useParams } from 'react-router-dom';

function UserDetail() {
  const { id } = useParams(); // 假设 URL 是 /user/123,则 id = "123"
  
  useEffect(() => {
    fetchUser(id); // 根据 id 请求数据
  }, [id]);

  return <div>当前用户 ID:{id}</div>;
}

3. 嵌套路由 —— 构建页面层级结构

场景:后台管理、仪表盘,侧边栏或顶部导航固定,只有内容区域随路由切换。

实现方式

  • 父组件内用 <Outlet /> 占位,表示子路由渲染的位置。
  • 子路由使用相对路径(不需要写父路径前缀)。
// 路由配置
<Route path="/dashboard" element={<DashboardLayout />}>
  <Route path="home" element={<DashboardHome />} />     // 实际路径 /dashboard/home
  <Route path="settings" element={<Settings />} />     // 实际路径 /dashboard/settings
</Route>

// DashboardLayout.jsx
import { Outlet, Link } from 'react-router-dom';

function DashboardLayout() {
  return (
    <div>
      <header>侧边栏 / 顶部导航</header>
      <main>
        <Outlet /> {/* 子路由在这里渲染 */}
      </main>
    </div>
  );
}

4. 重定向 —— 旧路径跳转到新路径

场景:网站改版、入口页跳转、用户登录后跳转。

实现方式:使用 <Navigate to="目标路径" replace /> 组件,replace 表示替换历史记录(用户点后退不会回到重定向前的旧路径)。

<Routes>
  <Route path="/old" element={<Navigate to="/new" replace />} />
  <Route path="/" element={<Navigate to="/home" replace />} />
  <Route path="/home" element={<Home />} />
  <Route path="/new" element={<NewPage />} />
</Routes>

5. 404 兜底 —— 匹配所有未定义路径

场景:用户输入了不存在的 URL,展示友好的“页面不存在”提示。

实现方式:使用 path="*" 放在所有具体路由的最后面(路由匹配从上到下)。

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="*" element={<NotFound />} />  {/* 放在最后 */}
</Routes>

// NotFound.jsx
function NotFound() {
  return <h2>404 - 您访问的页面不存在</h2>;
}

6. 鉴权拦截(路由守卫)—— 未登录跳转登录页

场景:某些页面(如个人中心、订单页)必须登录才能访问。

核心逻辑

  1. 封装一个 AuthRoute 组件,检查登录状态(如 token)。
  2. 若未登录,用 <Navigate> 跳转到 /login,并通过 state 把当前访问路径传过去。
  3. 登录成功后,从 state 取出原始路径,跳转回去,提升用户体验。
// 1. 封装鉴权组件 AuthRoute.jsx
import { Navigate, useLocation } from 'react-router-dom';

function AuthRoute({ children }) {
  const isLoggedIn = !!localStorage.getItem('token');
  const location = useLocation();

  if (!isLoggedIn) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  return children;
}

// 2. 路由配置
<Route
  path="/profile"
  element={
    <AuthRoute>
      <Profile />
    </AuthRoute>
  }
/>

// 3. 登录页逻辑 Login.jsx
import { useLocation, useNavigate } from 'react-router-dom';

function Login() {
  const location = useLocation();
  const navigate = useNavigate();
  const from = location.state?.from?.pathname || '/';

  const handleLogin = () => {
    // 登录成功后跳回来源页
    navigate(from, { replace: true });
  };

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

踩坑提醒state 传递时务必用 useLocation() 返回的对象,不要手动传 window.location(它是 DOM 对象,不可序列化,会报 DataCloneError)。


四、核心 API 总结

路由模式核心 API / 组件解决什么问题
懒加载lazy + Suspense首屏加载慢,代码体积大
动态路由:id + useParams()需要从 URL 读取参数(如详情页 ID)
嵌套路由<Outlet />页面公共布局复用,局部刷新
重定向<Navigate to="..." replace />路径变更、默认入口跳转
404 兜底path="*"匹配未知路径,提升用户体验
鉴权拦截自定义 AuthRoute + location.state未登录禁止访问特定页面,并支持登录后回跳

五、开发黄金原则

  1. 路由顺序很重要:具体路由写在前面,path="*" 写在最后。
  2. 导航别用 <a>:内部跳转务必使用 <Link>useNavigate(),否则会整页刷新丢失 SPA 状态。
  3. 状态传递用对象state 只传可序列化的数据(字符串、数字、普通对象),千万别传 DOM 元素或函数。

六、Vue vs React 路由核心差异

1. Vue 的“强制约定”

  • Vue 官方脚手架强制要求路由文件放在 src/router/index.js
  • 路由采用配置对象数组形式({ path: '/', component: Home })。
  • main.js 中通过 app.use(router) 全局注入,App.vue 只放 <router-view /> 占位符,完全不关心路由表具体内容
  • 特点:强制将路由逻辑从视图层剥离

2. React 的“灵活自由”

  • React 没有强制规定路由文件位置,你有两种主流选择:
    • 方式 A(常见):直接在 App.js 里写 <Routes><Route>,即“组件即路由”的思维。
    • 方式 B(更接近 Vue):在 src/router/index.js 里导出一个路由配置数组(React Router v6.4+ 支持 createBrowserRouter),然后在 main.jsApp.js 中引入。

核心差别

  • React 把路由写成 JSX 标签<Route path="/" element={<Home />} />
  • Vue 把路由写成 纯数据对象{ path: '/', component: Home }

七、React 启动链关系

理解 React 应用的启动顺序,有助于你更好地组织代码:

index.html(静态壳) → 加载 main.js(脚本入口) → 渲染 App.js(根组件)

1. index.html —— 浏览器入口

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>  <!-- React 挂载锚点 -->
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

它只做两件事:提供 div#root 容器和加载 main.js

2. main.js —— 应用脚本入口

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.createRoot(document.getElementById('root')).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

职责:初始化渲染引擎,挂载根组件。

3. App.js —— 根组件

import { Routes, Route } from 'react-router-dom';
import Home from './pages/Home';

function App() {
  return (
    <div>
      <Routes>
        <Route path="/" element={<Home />} />
      </Routes>
    </div>
  );
}
export default App;

职责:组织 UI 结构,承载路由与布局,是组件树的顶层。

维度main.jsApp.js
角色入口脚本(项目入口文件)根组件(Root Component)
是否组件❌ 不是组件,是执行代码✅ 是组件(函数/类)
核心职责初始化渲染引擎,挂载根节点组织 UI 结构,承载路由与布局
修改频率极少改动(如添加全局 Provider)频繁改动(新增页面路由)

结语

React Router 是 React 生态中最核心的路由库,掌握它几乎是每个 React 开发者的必修课。本文从路由选型、基础配置到高阶技巧(懒加载、动态路由、嵌套路由、鉴权拦截等)进行了系统梳理,并对比了 Vue 与 React 的路由设计哲学,希望能帮你建立起完整的知识体系。

最后送大家三句话

  • 路由顺序决定匹配优先级。
  • 内部导航用 Link,别用 a 标签。
  • 状态传递记得序列化。

如果觉得有用,欢迎点赞、收藏、转发,让更多小伙伴受益!我们下期再见 👋