React Router 全面指南 (v6.4+ & v7)
React Router 全面指南 (v6.4+ & v7)
📚 本指南旨在帮助开发者掌握 React Router 的最新特性 (v6.4+ Data APIs) 及企业级封装写法。
目录
一、引言
1.1 为什么需要路由? (SPA vs MPA)
在传统的多页应用 (MPA) 中,每次点击链接,浏览器都会向服务器请求一个新的 HTML 页面,导致页面刷新(白屏闪烁)。
React Router 帮助我们构建 单页应用 (SPA):
- 无刷新跳转:点击链接时,只通过 JavaScript 更新页面内容,无需重新加载整个页面。
- 体验如原生应用:丝滑的页面切换效果。
- URL 与 UI 同步:通过 URL (
/about) 直接定位到特定组件 (<About />)。
1.2 React Router v6.4+ (Data APIs)
这是 React Router 的一次革命性更新,引入了 Data APIs。这不仅是定义路由的新方式,更解决了 React 应用中常见的“加载瀑布流”问题。
核心优势:
- 并行加载:路由与数据获取并行进行,极大提升首屏速度。
- 配置化路由:使用对象数组 (
createBrowserRouter) 管理路由,比 JSX 写法更清晰、更易维护。 - 自动状态管理:内置
loading、error状态处理,减少大量样板代码。
1.3 React Router v7+三种使用模式
React Router 的这三种模式反映了它从一个简单的组件库演变为功能完备的数据框架的历程。
1. 三种模式的区别
| 模式 | 核心理念 | 使用场景 |
|---|---|---|
| Declarative (声明式) | 传统的组件化路由,使用 <Routes> 和 <Route> 直接写在 JSX 中。 | 简单的 CSR 应用,不需要路由驱动的数据预加载。 |
| Data (数据驱动) | React Router v6.4+ 推荐方式。使用 createBrowserRouter 定义路由对象。支持 loader(预加载数据)、action(数据修改)和 errorElement。 | 中大型应用,希望通过路由并行加载数据、处理表单提交和错误边界。 |
| Framework (框架模式) | React Router v7 (原 Remix)。将 React Router 作为全栈框架使用,处理 SSR(服务端渲染)、静态生成等。 | 需要 SSR、SEO 优化或复杂全栈逻辑的项目。 |
二、快速上手 (Data Router)
2.1 安装
1 | # 安装最新版 React Router DOM (包含了核心 React Router) |
提示:安装完成后,检查
package.json中的dependencies,确认版本号>= 6.4。
2.2 基本使用
与传统的组件式 (<BrowserRouter>) 不同,新版推荐使用对象配置。
src\router\index.js
1 | import { createBrowserRouter } from "react-router-dom"; |
src\App.jsx
1 | import { RouterProvider } from "react-router-dom"; |
三、路由核心基础 (Routing)
3.1 数据加载 (Loader)
注意: 这里展示的是基础概念,更深入的 Data API 用法请参考 第四章 Data API。
这是 Data APIs 的基础功能。它允许路由在渲染组件之前并行加载数据,彻底解决了 React 应用中常见的“瀑布流加载”问题。
为什么使用 loader 而不是 verifyEffect?
- 传统方式 (useEffect):
加载组件 JS->渲染组件(Loading)->发起 Fetch->等待数据->渲染真实内容。 - Loader 方式:
点击链接->并行加载组件 JS 和数据->渲染真实内容。
代码实现:
1 | // 1. 定义数据加载函数 (Loader) |
在组件中获取数据:
组件内部无需处理 loading 状态,因为 loader 执行完才会渲染组件(除非使用了 defer)。
1 | import { useLoaderData } from "react-router-dom"; |
3.2 路由跳转 (Navigation)
React Router 提供了两种主要的跳转方式:声明式和编程式。
1)声明式跳转
用于构建用户界面中的导航链接,类似于 HTML 的 <a> 标签,但不会刷新页面。
<Link>: 基础链接组件。<NavLink>: 特殊的 Link,自动感知是否处于激活状态(常用于菜单)。
1 | import { Link, NavLink } from "react-router-dom"; |
2)编程式跳转
useNavigate在事件处理函数 (Event Handler) 或副作用 (Effect) 中执行跳转。
1 | import { useNavigate } from "react-router-dom"; |
3.3 参数传递 (Params)
这是开发中最常见的需求,React Router 支持三种主要的传参方式。
1)路径参数 (Path Params)
参数直接拼接在 URL 中,如 /user/123。
- 优点:刷新页面参数不丢失,语义清晰。
- 定义:需要在路由配置中定义占位符
:id。
1️⃣路由定义:
1 | // router/index.jsx |
2️⃣跳转传参:
1 | <Link to="/user/123">查看用户 123</Link>; |
3️⃣获取参数:
1 | import { useParams } from "react-router-dom"; |
2)查询参数 (Query Params)
参数以 key=value 形式拼接在 ? 后,如 /list?page=1&sort=desc。
- 优点:无需修改路由定义,灵活多变。
1️⃣跳转传参:
1 | <Link to="/list?page=1&sort=desc">商品列表</Link>; |
2️⃣获取参数:
1 | import { useSearchParams } from "react-router-dom"; |
3)隐式状态 (State)
参数不显示在 URL 中,存储在 history state 对象里。
- 优点:可传递对象、数组等复杂数据,URL 干净。
- 缺点:刷新页面不丢失(很多误区认为会丢失,实际上 History State 刷新后依然存在),但复制链接给别人会丢失数据。
1️⃣跳转传参:
1 | <Link to="/profile" state={{ role: "admin", code: 9527 }}> |
2️⃣获取参数:
1 | import { useLocation } from "react-router-dom"; |
3.4 嵌套路由与 Outlet
嵌套路由是 React Router 最核心、最强大的功能。它让我们能够将 UI 的嵌套结构与 URL 的路径结构完美对应。
1)什么是嵌套路由?
在大多数应用中,界面通常是由多层嵌套的组件组成的。例如:
- URL:
/dashboard/settings - UI: 页面整体 -> 侧边栏布局 (
Parent) -> 设置面板 (Child)
React Router 通过 嵌套路由 自动帮我们将这种 URL 映射为组件层级:/dashboard -> 渲染 <DashboardLayout />/dashboard/settings -> 在 <DashboardLayout> 内部渲染 <Settings />
2)嵌套配置
我们在路由配置中使用 children 数组来定义子路由。
1 | // src/router/index.jsx |
3)父组件如何渲染子路由?
光配置了路由还不够,父组件必须明确指定“子组件显示在哪里”。如果不写 <Outlet />,子路由的内容将不会渲染。
1 | // src/layouts/DashboardLayout.jsx |
4)总结:路由匹配流程
当用户访问 /dashboard/settings 时:
- React Router 匹配到
/dashboard-> 渲染<DashboardLayout>。 - React Router 继续匹配
settings-> 找到<Settings>组件。 - 它将
<Settings>组件作为 children 传递给<Outlet />,最终页面结构如下:
1 | <DashboardLayout> |
3.5 路由懒加载
随着应用规模的增长,打包后的 JavaScript 文件(Chunk)会变得越来越大,导致首屏加载缓慢。 路由懒加载 是优化 React 应用性能的关键手段。
1)为什么要懒加载?
- 默认行为:构建工具(如 Viper/Webpack)会将所有页面组件打包进一个巨大的 JS 文件中。即使有些页面用户可能永远不会访问,浏览器也必须先下载这些代码。
- 懒加载行为:将代码分割成多个小块(Chunks)。只有当用户点击跳转到特定路由时,浏览器才去下载该页面对应的 JS 代码。
2)核心 API:React.lazy & Suspense
React 原生提供了 lazy 函数来实现动态导入,配合 Suspense 组件处理加载状态。
1 | import { lazy, Suspense } from "react"; |
注意:如果不使用
Suspense包裹,React 在等待组件加载时会抛出错误,导致应用崩溃。
3.6 路由模式
React Router 主要支持两种路由模式:History 模式 和 Hash 模式。在 Data API (v6.4+) 中,它们分别对应 createBrowserRouter 和 createHashRouter。
1)History 模式 (推荐)
这是现代 Web 应用的标准模式,使用 HTML5 History API (pushState, replaceState) 来管理 URL。
- 表现:URL 看起来像标准的路径,例如
example.com/user/123。 - 优点:URL 美观,符合用户习惯;SEO 友好。
- 缺点:需要服务器配置支持。因为是单页应用,当用户直接访问深层路径(如
/user/123)或刷新页面时,服务器必须返回index.html,否则会出现 404 错误。
代码示例:
1 | import { createBrowserRouter, RouterProvider } from "react-router-dom"; |
服务器配置示例 (Nginx):
1 | location / { |
2)Hash 模式
使用 URL 的 Hash 部分 (#) 来模拟一个完整的 URL。
- 表现:URL 带有
#号,例如example.com/#/user/123。 - 优点:兼容性极好(支持老旧浏览器);不需要服务器额外配置(因为
#后面的内容不会发送给服务器)。 - 缺点:URL 不够美观;SEO 较差;某些第三方登录回调可能不支持 Hash 路径。
代码示例:
1 | import { createHashRouter, RouterProvider } from "react-router-dom"; |
3)如何选择?
| 特性 | History 模式 (createBrowserRouter) | Hash 模式 (createHashRouter) |
|---|---|---|
| URL 外观 | 美观 (/path) | 带井号 (/#/path) |
| 服务器配置 | 需要 (Nginx/Apache rewrite) | 不需要 |
| SEO | 友好 | 较差 |
| 部署难度 | 中 | 低 |
| 推荐场景 | 公网项目、企业级应用 | 内部工具、演示 Demo、无法控制服务器配置时 |
4)拓展
1️⃣History 模式下为什么需要服务器配置支持,为什么当用户直接访问深层路径(如 /user/123)或刷新页面时,服务器必须返回 index.html,否则会出现 404 错误。
答:当你直接在浏览器地址栏输入 http://example.com/user/123 并回车(或按刷新键):
- 浏览器:这被视为一次全新的访问。浏览器会老老实实向服务器发送一个 HTTP GET 请求:”请给我 /user/123 这个文件”。
- 服务器:去它的硬盘文件系统里找
- 它找有没有 user 文件夹?没有。
- 它找有没有 user/123 文件?没有。
- 它找有没有 user/123.html?也没有。
- 因为 SPA 项目打包后,服务器上通常只有一个 index.html 和一堆 .js/.css 文件,根本不存在物理上的 /user/123 目录。
- 结果:服务器诚实地返回了 404 Not Found。
3.7 404 页面配置
在 SPA 应用中,当用户访问了未定义的路径时,如果不做处理,页面可能会显示一片空白。我们需要配置一个 Catch-All 路由 来展示友好的 404 页面。
在 React Router v6 中,使用 path: "*" 即可匹配任意路径。由于 v6 内部有智能的路由排名算法(Ranking Algorithm),你不需要在这个路由上加 exact,也不强求将它放在路由数组的最后(但在 v5 中必须放在最后)。
1)基础配置
1 | // src/router/index.jsx |
2)创建优雅的 404 组件
简单的 404 页面应该包含错误提示和返回首页的按钮。
1 | // src/pages/NotFound.jsx |
3)重定向到首页
有时候我们不希望显示 404 页面,而是直接将用户重定向回首页(例如在后台管理系统中)。可以使用 <Navigate> 组件。
1 | { |
四、Data API (进阶)
React Router 6.4+ 引入的 Data API 不仅仅是关于路由跳转,它提供了一套完整的数据生命周期管理方案。这使得我们可以在路由层面处理数据获取、提交和错误处理,大大减少组件内部的副作用代码。
4.1 数据加载 (Loader)
注意:Loader 的基础用法已在 3.1 节 中介绍。
Loader 是 Data API 的读取端。它在路由渲染前运行,为组件提供数据。
核心优势:
- 并行加载:路由组件和数据并行请求,消除瀑布流。
- 数据/UI 分离:组件只负责接收数据渲染,不负责 Fetch。
4.2 数据提交 (Action)
Action 是 Data API 的写入端,用于处理非 GET 请求(如 POST, PUT, DELETE)。它与 <Form> 组件配合,能在不手动编写 onSubmit 和 fetch 的情况下完成数据提交。
1)定义 Action
1 | // src/pages/Login.jsx |
2)绑定 Config
1 | // router/index.jsx |
3)使用 Form 组件
1 | import { Form } from "react-router-dom"; |
4.3 无导航交互 (useFetcher)
通常点击链接或提交表单会导致页面跳转。但有些操作我们不希望跳转页面,比如:点赞、加入购物车、Newsletter 订阅。这时使用 useFetcher。
提供了 fetcher.Form、fetcher.submit、fetcher.load 等方法,可以在不导航的情况下触发 loader 或 action。
1 | import { useFetcher } from "react-router-dom"; |
4.4 错误边界 (ErrorElement)
每个路由都可以定义一个 errorElement。当 loader、action 或组件渲染过程中抛出错误时,React Router 会捕获它并渲染 errorElement,而不是让整个页面白屏。
1 | { |
五、封装式路由配置 (Best Practice)
在真实项目中,我们将路由配置抽离到独立文件中管理,使其结构更清晰。
5.1 目录结构
推荐在 src/router 目录下管理路由:
1 | src/ |
5.2 路由配置文件
利用 createBrowserRouter 和 lazy 实现配置化与懒加载的完美结合。
1 | // src/router/index.jsx |
5.3 接入
1 | // App.jsx |
六、进阶与优化
在封装式写法中,我们可以创建一个包装组件来保护路由。
6.1 路由守卫 (Protected Routes)
1 | // components/AuthGuard.jsx |
6.2 全局 Loading 状态
使用 useNavigation 监听全局路由跳转状态。
1 | import { useNavigation, Outlet } from "react-router-dom"; |
七、常见误区 (Common Pitfalls)
7.1 错误使用 Layout 组件 (Context 丢失)
很多初学者容易犯的一个错误是:将布局组件 (Layout / Menu) 防止在 RouterProvider 之外。
错误写法:
1 | // App.jsx |
问题后果:
- 无法使用
<Link>/<NavLink>:点击菜单会报错useHref() may be used only in the context of a <Router> component. - 无法使用 hooks:
useLocation、useNavigate等 Hook 均无法工作。 - 路由状态无法共享:布局组件无法感知当前的路由变化。
- 如果Layout中是固定的内容,不涉及路由跳转等,布局组件则可以写在
RouterProvider之外
最佳实践:是利用 嵌套路由 (Nested Routes) 和 <Outlet />。
第一步:创建布局组件 (src/components/Layout.jsx)
利用 <Outlet /> 渲染子路由内容:
1 | import { Outlet, Link } from "react-router-dom"; |
第二步:配置嵌套路由 (src/router/index.jsx)
1 | import { createBrowserRouter } from "react-router-dom"; |
第三步:入口文件 (src/main.jsx)
入口文件保持简洁,只需注入 router:
1 | import React from "react"; |
八、总结
React Router v6.4+ 定义了现代 React 应用的路由标准:
- 使用
createBrowserRouter进行集中式路由管理。 - 利用
loader提前加载数据,提升首屏体验。 - 结合
Suspense和lazy实现代码分割。 - 目录结构规范化,将路由逻辑与 UI 组件分离。
掌握这套模式,你将能构建出性能更优、架构更清晰的 React 应用。
