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

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 示例 特点
BrowserRouter createBrowserRouter http://localhost/home 依赖 History API,URL 干净,符合 RESTful 风格;部署时需要服务端配置回退到 index.html
HashRouter createHashRouter http://localhost/#/home 利用 hash 路由,无需服务端支持,适合静态站点

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


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

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

jsx 复制代码
// 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 图标)。
jsx 复制代码
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() 获取参数。
jsx 复制代码
// 路由配置
<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 /> 占位,表示子路由渲染的位置。
  • 子路由使用相对路径(不需要写父路径前缀)。
jsx 复制代码
// 路由配置
<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 表示替换历史记录(用户点后退不会回到重定向前的旧路径)。

jsx 复制代码
<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="*" 放在所有具体路由的最后面(路由匹配从上到下)。

jsx 复制代码
<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 取出原始路径,跳转回去,提升用户体验。
jsx 复制代码
// 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 应用的启动顺序,有助于你更好地组织代码:

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

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

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 ------ 应用脚本入口

jsx 复制代码
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 ------ 根组件

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

结语

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

最后送大家三句话

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

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

相关推荐
xm_xm_xm_112 小时前
react17版本以前类组件常用操作
前端·javascript·react.js
Damai1 天前
Next.js 切换动态路由后,为什么页面还保留着上一个分类的数据?
前端·react.js
用户298698530141 天前
React 中 Word 文档转图片的实践方案
前端·javascript·react.js
专业抄代码选手1 天前
06|双缓冲与 Commit:让 Fiber 支持第二次渲染
前端·javascript·react.js
专业抄代码选手1 天前
05|把递归渲染拆成 Fiber:让一棵大树可以暂停
前端·javascript·react.js
墨白曦煜1 天前
智能体架构范式总结(React、Plan-And-Solve、Reflection)
前端·react.js·前端框架
zzzzzz3101 天前
react-bits:把动效组件库当成前端表达的参考系
react.js·开源·动效
Shinner欣儿2 天前
React18 和 19 新特性结合看
前端·react.js
光影少年2 天前
react navite环境与工程化
前端·react native·react.js