一、React Router 重定向基础概念:理解核心原理与应用价值
1.1 什么是重定向及其典型应用场景
重定向是指在用户访问某个 URL 时, 自动将其导航到另一个 URL 的机制。在单页应用 (SPA) 中, 重定向是路由系统的核心能力之一, 常用于以下场景:
- 用户未登录时跳转到登录页
- 页面迁移后引导用户访问新地址
- 表单提交成功后跳转至结果页
- 权限不足时跳转至首页或提示页
- 旧版本 URL 兼容与平滑迁移
掌握 "如何在React Router 中设置重定向?" 是构建健壮 SPA 的必备技能, 本文将系统讲解 v5 与 v6 两套主流方案的差异、用法与最佳实践。
1.2 React Router 主要版本演进与差异
React Router 经历了多次大版本迭代, 重定向 API 也发生了较大变化。下表汇总了不同版本的核心 API:
| 版本 | 重定向组件 | 编程式 API | 维护状态 |
|------|------------|------------|----------|
| v4 / v5 |<Redirect>|this.props.history.push| 仍可用但已停止维护 |
| v6.0 ~ 6.3 |<Navigate>|useNavigate| 当前主流 |
| v6.4+ |<Navigate>+redirect()|useNavigate| 推荐方案 |
1.3 重定向流程概览
通过下面的流程图, 可以直观理解一次重定向请求的完整生命周期:
二、React Router v5 重定向方案:经典实现方式详解
2.1 使用 Redirect 组件实现声明式重定向
在 React Router v5 中,<Redirect>组件是最常用的声明式重定向方式。它可以直接渲染在组件树中, 触发时自动跳转到指定路径。
import { Redirect } from "react-router-dom"; function LoginGate({ isLogged }) { if (!isLogged) { return <Redirect to="/login" />; } return <Dashboard />; }<Redirect>常用属性:
to: 目标路径, 字符串或对象from: 仅在 Switch 内部使用, 匹配源路径push: 是否以 push 方式跳转 (默认 false, 即 replace)exact: 是否精确匹配 from 路径
2.2 在 Switch 中使用 from 与 to 实现路由级重定向
当需要批量处理旧路径时,<Switch>内的<Redirect from to>是最简洁的写法:
import { Switch, Route, Redirect } from "react-router-dom"; <Switch> <Redirect from="/old-home" to="/home" exact /> <Redirect from="/legacy/:id" to="/product/:id" /> <Route path="/home" component={Home} /> <Route path="/product/:id" component={Product} /> </Switch>注意事项:
from必须配合<Switch>使用- 命名参数会被自动传递到
to中 - 顺序很重要, 更具体的规则要写在前面
2.3 编程式重定向 this.props.history
当需要在事件回调或异步操作后跳转时, 使用编程式重定向更灵活。class 组件通过 props 获取 history:
class Login extends React.Component { handleLogin = async () => { const { history } = this.props; const ok = await loginApi(); if (ok) { history.push("/dashboard"); } }; render() { return <button onClick={this.handleLogin}>登录</button>; } }函数组件中可通过useHistory获取 history 对象:
import { useHistory } from "react-router-dom"; function LoginButton() { const history = useHistory(); return <button onClick={() => history.push("/dashboard")}>登录</button>; }三、React Router v6 重定向方案:现代写法与最佳实践
3.1 使用 Navigate 组件替代 Redirect
React Router v6 移除了<Redirect>组件, 引入了<Navigate>作为替代。语义更清晰, API 更简洁。
import { Navigate } from "react-router-dom"; function LoginGate({ isLogged }) { if (!isLogged) { return <Navigate to="/login" replace />; } return <Dashboard />; }<Navigate>的核心属性:
to: 目标路径, 可为字符串或对象{ pathname, search, hash, state }replace: 是否替换历史记录 (boolean), 默认 falsestate: 传递给目标路由的状态数据
3.2 在路由配置中通过 index 路由实现初始重定向
v6 不再支持<Redirect from to>, 但可以通过index路由配合<Navigate>实现相同效果。
import { Routes, Route, Navigate } from "react-router-dom"; <Routes> <Route path="/" element={<Navigate to="/home" replace />} /> <Route path="/home" element={<Home />} /> <Route path="/login" element={<Login />} /> <Route path="*" element={<NotFound />} /> </Routes>对于带参数的旧路径迁移, 需要单独组件处理:
import { useParams, Navigate } from "react-router-dom"; function LegacyRedirect() { const { id } = useParams(); return <Navigate to={`/product/${id}`} replace />; } <Route path="/legacy/:id" element={<LegacyRedirect />} />3.3 编程式重定向 useNavigate Hook
useNavigate是 v6 推荐的编程式跳转 API, 签名更紧凑:
import { useNavigate } from "react-router-dom"; function Login() { const navigate = useNavigate(); const handleSubmit = async (e) => { e.preventDefault(); const ok = await loginApi(); if (ok) { navigate("/dashboard", { replace: true, state: { from: "login" } }); } }; return <form onSubmit={handleSubmit}>...</form>; }useNavigate的两种调用形式:
navigate(path): 字符串路径或对象navigate(delta): 数字, 如navigate(-1)等价于浏览器后退
3.4 通过 loader 实现数据层重定向 (v6.4+)
v6.4 引入的数据路由器提供了redirect()工具函数, 可在 loader 或 action 中触发重定向。这种方式将重定向逻辑前置到数据层, 避免组件闪烁。
import { createBrowserRouter, redirect } from "react-router-dom"; const router = createBrowserRouter([ { path: "/admin", loader: async () => { const user = await fetchUser(); if (!user.isAdmin) { throw redirect("/no-permission"); } return null; }, element: <Admin />, }, ]);四、复杂业务场景下的重定向实战:权限与状态管理
4.1 基于登录态的权限重定向
完整的权限重定向通常需要结合全局状态管理。下面示例使用 Context + Navigate 实现:
import { useContext } from "react"; import { Routes, Route, Navigate, useLocation } from "react-router-dom"; import { AuthContext } from "./auth"; function RequireAuth({ children }) { const { user } = useContext(AuthContext); const location = useLocation(); if (!user) { return <Navigate to="/login" state={{ from: location }} replace />; } return children; } <Routes> <Route path="/login" element={<Login />} /> <Route path="/dashboard" element={<RequireAuth><Dashboard /></RequireAuth>} /> </Routes>4.2 自定义 ProtectedRoute 守卫组件
更通用的守卫组件可以同时处理权限校验与角色判断:
function ProtectedRoute({ children, roles }) { const { user } = useContext(AuthContext); const location = useLocation(); if (!user) { return <Navigate to="/login" state={{ from: location.pathname }} replace />; } if (roles && !roles.includes(user.role)) { return <Navigate to="/403" replace />; } return children; } <Route path="/admin" element={ <ProtectedRoute roles={["admin"]}> <Admin /> </ProtectedRoute> } />4.3 带状态参数的重定向与取值
通过state传递数据到目标路由, 登录回跳场景特别有用:
// 发送方 navigate("/result", { state: { orderId: 123, status: "success" } }); // 或使用 Navigate 组件 <Navigate to="/result" state={{ orderId: 123 }} />接收方通过useLocation取值:
import { useLocation } from "react-router-dom"; function Result() { const { state } = useLocation(); const orderId = state?.orderId; return <div>订单号: {orderId}</div>; }4.4 重定向决策流程图
下图展示了带权限判断的完整重定向决策链路:
五、最佳实践与常见问题排查:让重定向更稳健
5.1 避免重定向死循环
死循环通常出现在多个守卫互相重定向的情况。排查思路:
- 检查守卫逻辑是否有矛盾 (如 A 守卫要求登录跳 /login, /login 又要求登录跳 /home)
- 登录页本身不应被 RequireAuth 包裹
- 使用 replace 而非 push 避免历史栈无限增长
// 反例: 会导致死循环 <Route path="/login" element={<RequireAuth><Login /></RequireAuth>} /> // 正例 <Route path="/login" element={<Login />} /> <Route path="/dashboard" element={<RequireAuth><Dashboard /></RequireAuth>} />5.2 SEO 与服务端渲染的重定向处理
在 SSR 场景下, 浏览器端的重定向方案不生效, 需要在服务端或 SSR 框架层面处理:
- Next.js: 使用
getServerSideProps中的redirect返回值 - Remix: 在 loader 中使用
redirect() - 自定义 SSR: 在服务端响应中设置 301/302 状态码与 Location 头
import { redirect } from "@remix-run/node"; export async function loader({ request }) { const user = await getUser(request); if (!user) { return redirect("/login", { status: 302, headers: { "Cache-Control": "no-store" } }); } return { user }; }5.3 调试重定向问题的实用技巧
- 在浏览器控制台使用
window.location查看真实 URL - 在 React DevTools 中检查
<Navigate>是否被渲染 - 临时打印
useLocation()的输出, 确认 state 与 pathname - 使用 history API 的监听器记录跳转链路
- 检查是否在 React.StrictMode 下出现重复渲染 (v6 已优化)
5.4 重定向方案选择决策图
面对不同场景, 可参照下图选择合适的重定向方案:
5.5 关键差异对比表
最后通过一张对比表总结各方案的核心区别:
| 维度 | v5 Redirect | v6 Navigate | v6.4 loader redirect |
|------|-------------|-------------|----------------------|
| 触发位置 | JSX 渲染 | JSX 渲染 | 数据层 |
| 替换历史 | push 属性 | replace 属性 | options.replace |
| 携带状态 | 不支持 | state 属性 | 不支持 |
| 携带参数 | from 自动传递 | 需手动拼接 | 需手动拼接 |
| 适用场景 | 简单跳转 | 现代应用 | 数据驱动跳转 |