🚀 React Router 是React生态中最受欢迎的路由库,用于构建单页面应用(SPA)的页面导航系统。版本6是一次重大升级,引入了全新的API和更简洁的语法。本文将带你了解React Router 6的核心概念和实用技巧,助你构建现代化的React应用。
📚 单页应用与路由基础
💡 单页应用的工作原理
SPA(Single Page Application)是指整个应用只有一个HTML页面,页面切换时只做局部更新而不刷新整个页面。
单页应用的优势:
- 🚀 更快的用户体验 - 无需频繁刷新页面,避免白屏时间
- 📊 减少服务器压力 - 只需加载必要的数据,而非整个页面
- 🎯 更流畅的交互 - 类似原生应用的体验,无缝过渡
🧭 路由的本质与实现方式
后端路由:
// Express后端路由示例
app.get('/users', (req, res) => {
// 处理用户列表请求
})
前端路由:
// React Router前端路由示例
<Route path="/users" element={<UserList />} />
关键区别:前端路由在浏览器中处理URL变化,不会向服务器发送请求,而是在本地切换显示的组件。
🔄 React Router 6 的革新
React Router 6相比5.x版本有哪些重要变化?
特性 | React Router 5 | React Router 6 |
---|---|---|
路由容器 | <Switch> | <Routes> |
路由定 义 | <Route component={Home}> | <Route element={<Home/>}> |
重定向 | <Redirect> | <Navigate> |
路由API | 基于组件的API | 基于Hooks的API |
嵌套路由 | 需要确保路径匹配 | 自动相对路径 |
路由配置 | 主要基于组件声明 | 支持对象配置(useRoutes) |
🧩 路由系统的核心组件
🌐 路由环境容器
<BrowserRouter>
利用 HTML5 的 history API (pushState
, replaceState
等) 实现UI与URL的同步,是最常用的路由容器。
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
而 <HashRouter>
使用URL的哈希部分(即 #
后面的部分)来保持UI与URL的同步,适用于不支持HTML5 history API的老旧浏览器。
<HashRouter>
的URL中会包含 #
符号,如 http://example.com/#/about
,而 <BrowserRouter>
的URL更干净:http://example.com/about
🛣️ 路由定义与匹配
<Routes>
和 <Route>
是React Router 6中定义路由规则的核心组件:
import { Routes, Route } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
);
}
嵌套路由的表达方式更加直观:
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />}>
<Route path="stats" element={<DashboardStats />} />
<Route path="profile" element={<UserProfile />} />
</Route>
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
🔗 页面导航与链接组件
<Link>
组件的 基本应用
import { Link } from "react-router-dom";
function Navigation() {
return (
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于我们</Link>
<Link to="/dashboard">控制台</Link>
</nav>
);
}
<NavLink>
与激活状态样式
import { NavLink } from "react-router-dom";
function Navigation() {
return (
<nav>
<NavLink
to="/"
className={({ isActive }) => isActive ? "active-link" : "normal-link"}
>
首页
</NavLink>
<NavLink
to="/about"
style={({ isActive }) => ({
fontWeight: isActive ? "bold" : "normal",
color: isActive ? "#ff0000" : "#000000"
})}
>
关于我们
</NavLink>
</nav>
);
}
路由重定向与条件导航
import { Navigate } from "react-router-dom";
function ProtectedPage({ isAuthenticated }) {
if (!isAuthenticated) {
// 如果未登录,重定向到登录页
return <Navigate to="/login" replace={true} />;
}
return <div>这是受保护的页面内容</div>;
}
📤 嵌套路由与内容渲染
<Outlet>
是一个重要的概念,它为嵌套路由提供了内容渲染位置:
import { Outlet, Link } from "react-router-dom";
function Layout() {
return (
<div>
<header>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/dashboard">控制台</Link>
</nav>
</header>
<main>
{/* 子路由组件将在这里渲染 */}
<Outlet />
</main>
<footer>© 2025 我的应用</footer>
</div>
);
}
🪝 路由钩子与实用功能
🧭 编程式导航与历史控制
useNavigate
钩子允许在事件处理程序中进行路由导航:
import { useNavigate } from "react-router-dom";
function LoginForm() {
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
// 登录逻辑...
if (loginSuccess) {
// 登录成功后跳转到首页
navigate("/dashboard", {
replace: true, // 替换当前历史记录
state: { from: "login" } // 传递状态数据
});
}
};
return (
<form onSubmit={handleSubmit}>
{/* 表单内容 */}
<button type="submit">登录</button>
<button type="button" onClick={() => navigate(-1)}>返回上一页</button>
</form>
);
}
📋 声明式与配置式路由管理
useRoutes
钩子提供了一种配置式的路由定义方式:
import { useRoutes } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Dashboard from "./pages/Dashboard";
import DashboardStats from "./pages/DashboardStats";
import UserProfile from "./pages/UserProfile";
import NotFound from "./pages/NotFound";
import Layout from "./components/Layout";
function App() {
const routes = useRoutes([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: "about", element: <About /> },
{
path: "dashboard",
element: <Dashboard />,
children: [
{ path: "stats", element: <DashboardStats /> },
{ path: "profile", element: <UserProfile /> }
]
},
{ path: "*", element: <NotFound /> }
]
}
]);
return routes;
}
📝 URL参数与数据传递
路径参数的获取
import { useParams } from "react-router-dom";
function ProductDetail() {
// 路由定义: <Route path="/products/:productId" element={<ProductDetail />} />
const { productId } = useParams();
return (
<div>
<h1>产品详情</h1>
<p>正在查看产品ID: {productId}</p>
</div>
);
}
查询参数的处理
import { useSearchParams } from "react-router-dom";
function ProductList() {
const [searchParams, setSearchParams] = useSearchParams();
// 获取当前URL中的查询参数
const category = searchParams.get("category") || "all";
const page = parseInt(searchParams.get("page") || "1", 10);
const handleCategoryChange = (newCategory) => {
// 更新查询参数
setSearchParams({ category: newCategory, page: 1 });
};
const handleNextPage = () => {
setSearchParams({ category, page: page + 1 });
};
return (
<div>
<div>
<button onClick={() => handleCategoryChange("all")}>全部</button>
<button onClick={() => handleCategoryChange("electronics")}>电子产品</button>
<button onClick={() => handleCategoryChange("clothing")}>服装</button>
</div>
<div>当前分类: {category}, 页码: {page}</div>
{/* 产品列表 */}
<button onClick={handleNextPage}>下一页</button>
</div>
);
}
📍 当前位置信息的获取与使用
useLocation
钩子提供对当前URL位置的完整访问:
import { useLocation } from "react-router-dom";
function Analytics() {
const location = useLocation();
// 当URL变化时记录页面访问
React.useEffect(() => {
// 发送页面访问事件到分析服务
trackPageView({
pathname: location.pathname,
search: location.search
});
}, [location]);
return (
<div>
<p>当前路径: {location.pathname}</p>
<p>查询参数: {location.search}</p>
{location.state && <p>状态数据: {JSON.stringify(location.state)}</p>}
</div>
);
}
🎯 路由高级应用模式
🔐 权限控制与路由守卫
保护路由是常见的应用需求,React Router 6可以轻松实现:
import { Navigate, Outlet, useLocation } from "react-router-dom";
function ProtectedRoute({ isAuthenticated }) {
const location = useLocation();
if (!isAuthenticated) {
// 重定向到登录页,并记住尝试访问的页面
return <Navigate to="/login" state={{ from: location }} replace />;
}
// 渲染子路由
return <Outlet />;
}
// 在路由配置中使用
function App() {
const isAuthenticated = checkAuthStatus(); // 检查用户是否已登录
return (
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
{/* 受保护的路由 */}
<Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<UserProfile />} />
<Route path="/settings" element={<Settings />} />
</Route>
<Route path="/" element={<Home />} />
</Routes>
);
}
🔄 代码分割与性能优化
懒加载是提高应用性能的重要方式:
import React, { Suspense, lazy } from "react";
import { Routes, Route } from "react-router-dom";
// 懒加载路由组件
const Home = lazy(() => import("./pages/Home"));
const About = lazy(() => import("./pages/About"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
function App() {
return (
<Suspense fallback={<div>页面加载中...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}
懒加载的主要优势:
- 🚀 减小初始加载包体积 - 只加载首屏需要的组件
- ⚡ 提升首屏加载速度 - 将非关键组件的加载推迟到需要时
- 📱 优化移动端体验 - 特别适合网络条件受限的场景
🧪 自定义路由工具与扩展
可以创建自定义钩子来封装和增强路由功能:
import { useLocation, useNavigate, useParams } from "react-router-dom";
// 结合多个路由钩子的自定义钩子
function useRouter() {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return {
location,
navigate,
params,
query: Object.fromEntries(new URLSearchParams(location.search)),
// 便捷导航方法
push: (to) => navigate(to),
replace: (to) => navigate(to, { replace: true }),
goBack: () => navigate(-1)
};
}
// 使用示例
function ProductPage() {
const router = useRouter();
const { productId } = router.params;
const { category } = router.query;
return (
<div>
<button onClick={router.goBack}>返回</button>
<h1>产品ID: {productId}</h1>
<p>分类: {category}</p>
</div>
);
}
📝 路由架构设计模式
🧠 组织复杂应用的路由结构
大型应用中,路由结构的组织至关重要:
// src/routes/index.jsx - 集中管理所有路由
import { lazy } from "react";
import MainLayout from "../layouts/MainLayout";
import AdminLayout from "../layouts/AdminLayout";
import ProtectedRoute from "../components/ProtectedRoute";
import LoginPage from "../pages/LoginPage";
// 懒加载页面组件
const HomePage = lazy(() => import("../pages/HomePage"));
const AboutPage = lazy(() => import("../pages/AboutPage"));
const DashboardPage = lazy(() => import("../pages/DashboardPage"));
const UserManagementPage = lazy(() => import("../pages/UserManagementPage"));
const NotFoundPage = lazy(() => import("../pages/NotFoundPage"));
// 导出路由配置
export const routes = [
{
path: "/",
element: <MainLayout />,
children: [
{ index: true, element: <HomePage /> },
{ path: "about", element: <AboutPage /> },
{ path: "login", element: <LoginPage /> },
]
},
{
path: "/admin",
element: <ProtectedRoute><AdminLayout /></ProtectedRoute>,
children: [
{ index: true, element: <DashboardPage /> },
{ path: "users", element: <UserManagementPage /> },
]
},
{ path: "*", element: <NotFoundPage /> }
];
🧩 模块化路由配置的优势
将路由按功能模块拆分,能提高代码的可维护性:
// src/routes/mainRoutes.js
import MainLayout from "../layouts/MainLayout";
import HomePage from "../pages/HomePage";
import AboutPage from "../pages/AboutPage";
export const mainRoutes = {
path: "/",
element: <MainLayout />,
children: [
{ index: true, element: <HomePage /> },
{ path: "about", element: <AboutPage /> },
]
};
// src/routes/adminRoutes.js
import AdminLayout from "../layouts/AdminLayout";
import DashboardPage from "../pages/DashboardPage";
import UserManagementPage from "../pages/UserManagementPage";
import ProtectedRoute from "../components/ProtectedRoute";
export const adminRoutes = {
path: "/admin",
element: <ProtectedRoute><AdminLayout /></ProtectedRoute>,
children: [
{ index: true, element: <DashboardPage /> },
{ path: "users", element: <UserManagementPage /> },
]
};
// src/routes/index.js
import { mainRoutes } from "./mainRoutes";
import { adminRoutes } from "./adminRoutes";
import NotFoundPage from "../pages/NotFoundPage";
import LoginPage from "../pages/LoginPage";
export const routes = [
mainRoutes,
adminRoutes,
{ path: "/login", element: <LoginPage /> },
{ path: "*", element: <NotFoundPage /> }
];