Appearance
路由配置与嵌套路由
概述
React Router v6的嵌套路由是构建复杂单页应用的核心特性。它允许创建层次化的路由结构,实现布局共享和模块化的路由管理。本文深入探讨路由配置方法和嵌套路由的各种使用模式。
路由配置方式
1. JSX配置方式
最直观的路由配置方式,适合简单到中等复杂度的应用:
jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
{/* 根路由 */}
<Route path="/" element={<Layout />}>
{/* 嵌套路由 */}
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="contact" element={<Contact />} />
{/* 用户相关路由 */}
<Route path="users" element={<UsersLayout />}>
<Route index element={<UsersList />} />
<Route path=":id" element={<UserProfile />} />
<Route path=":id/edit" element={<EditUser />} />
<Route path="new" element={<CreateUser />} />
</Route>
{/* 产品相关路由 */}
<Route path="products" element={<ProductsLayout />}>
<Route index element={<ProductsList />} />
<Route path="category/:category" element={<CategoryProducts />} />
<Route path=":id" element={<ProductDetail />} />
<Route path=":id/reviews" element={<ProductReviews />} />
</Route>
</Route>
{/* 认证路由(独立布局) */}
<Route path="auth" element={<AuthLayout />}>
<Route path="login" element={<Login />} />
<Route path="register" element={<Register />} />
<Route path="forgot-password" element={<ForgotPassword />} />
</Route>
{/* 404路由 */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}2. 对象配置方式
使用createBrowserRouter进行配置,适合大型复杂应用:
jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <RootErrorBoundary />,
children: [
{
index: true,
element: <HomePage />
},
{
path: 'dashboard',
element: <DashboardLayout />,
children: [
{
index: true,
element: <DashboardHome />
},
{
path: 'analytics',
element: <Analytics />,
loader: analyticsLoader
},
{
path: 'reports',
element: <Reports />
}
]
},
{
path: 'users',
element: <UsersLayout />,
children: [
{
index: true,
element: <UsersList />,
loader: usersListLoader
},
{
path: ':userId',
element: <UserDetail />,
loader: ({ params }) => userDetailLoader(params.userId),
children: [
{
index: true,
element: <UserOverview />
},
{
path: 'posts',
element: <UserPosts />,
loader: ({ params }) => userPostsLoader(params.userId)
},
{
path: 'settings',
element: <UserSettings />
}
]
}
]
}
]
},
{
path: '/admin',
element: <AdminLayout />,
errorElement: <AdminErrorBoundary />,
children: [
// 管理员路由配置
]
}
]);
function App() {
return <RouterProvider router={router} />;
}3. 动态路由配置
根据用户权限或配置动态生成路由:
jsx
import { createBrowserRouter } from 'react-router-dom';
// 路由配置生成函数
function createAppRouter(user, permissions, features) {
const baseRoutes = [
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: 'profile', element: <Profile /> }
]
}
];
// 根据权限添加路由
if (permissions.includes('read:users')) {
baseRoutes[0].children.push({
path: 'users',
element: <UsersLayout />,
children: [
{ index: true, element: <UsersList /> },
{ path: ':id', element: <UserDetail /> }
]
});
// 用户管理权限
if (permissions.includes('write:users')) {
baseRoutes[0].children
.find(route => route.path === 'users')
.children.push(
{ path: ':id/edit', element: <EditUser /> },
{ path: 'new', element: <CreateUser /> }
);
}
}
// 根据功能开关添加路由
if (features.analyticsEnabled) {
baseRoutes[0].children.push({
path: 'analytics',
element: <Analytics />
});
}
// 管理员路由
if (user.role === 'admin') {
baseRoutes.push({
path: '/admin',
element: <AdminLayout />,
children: [
{ index: true, element: <AdminDashboard /> },
{ path: 'users', element: <AdminUsers /> },
{ path: 'settings', element: <AdminSettings /> }
]
});
}
return createBrowserRouter(baseRoutes);
}
// 使用
function App() {
const { user, permissions, features } = useAuth();
const router = useMemo(
() => createAppRouter(user, permissions, features),
[user, permissions, features]
);
return <RouterProvider router={router} />;
}嵌套路由详解
1. Outlet组件
Outlet是嵌套路由的核心,用于渲染匹配的子路由:
jsx
import { Outlet, Link, useLocation } from 'react-router-dom';
function DashboardLayout() {
const location = useLocation();
return (
<div className="dashboard-layout">
{/* 侧边栏导航 */}
<aside className="sidebar">
<nav>
<Link
to="/dashboard"
className={location.pathname === '/dashboard' ? 'active' : ''}
>
Overview
</Link>
<Link
to="/dashboard/analytics"
className={location.pathname === '/dashboard/analytics' ? 'active' : ''}
>
Analytics
</Link>
<Link
to="/dashboard/reports"
className={location.pathname === '/dashboard/reports' ? 'active' : ''}
>
Reports
</Link>
<Link
to="/dashboard/settings"
className={location.pathname === '/dashboard/settings' ? 'active' : ''}
>
Settings
</Link>
</nav>
</aside>
{/* 主要内容区域 */}
<main className="main-content">
<header className="content-header">
<h1>Dashboard</h1>
<BreadcrumbNav />
</header>
{/* 子路由在这里渲染 */}
<div className="content-body">
<Outlet />
</div>
</main>
</div>
);
}
// 面包屑导航
function BreadcrumbNav() {
const location = useLocation();
const generateBreadcrumbs = () => {
const pathSegments = location.pathname.split('/').filter(Boolean);
const breadcrumbs = [];
let currentPath = '';
pathSegments.forEach((segment, index) => {
currentPath += `/${segment}`;
// 路径映射到显示名称
const displayNames = {
dashboard: 'Dashboard',
analytics: 'Analytics',
reports: 'Reports',
settings: 'Settings'
};
breadcrumbs.push({
path: currentPath,
name: displayNames[segment] || segment.charAt(0).toUpperCase() + segment.slice(1),
isLast: index === pathSegments.length - 1
});
});
return breadcrumbs;
};
const breadcrumbs = generateBreadcrumbs();
return (
<nav className="breadcrumb">
<Link to="/">Home</Link>
{breadcrumbs.map(crumb => (
<span key={crumb.path}>
<span className="separator"> / </span>
{crumb.isLast ? (
<span className="current">{crumb.name}</span>
) : (
<Link to={crumb.path}>{crumb.name}</Link>
)}
</span>
))}
</nav>
);
}2. Index路由
Index路由渲染在父路由的确切路径上:
jsx
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
{/* index路由匹配父路由的确切路径 */}
<Route index element={<Home />} /> {/* 匹配 "/" */}
<Route path="users" element={<UsersLayout />}>
<Route index element={<UsersList />} /> {/* 匹配 "/users" */}
<Route path=":id" element={<UserDetail />} /> {/* 匹配 "/users/123" */}
</Route>
<Route path="products" element={<ProductsLayout />}>
<Route index element={<ProductsList />} /> {/* 匹配 "/products" */}
<Route path="featured" element={<FeaturedProducts />} /> {/* 匹配 "/products/featured" */}
<Route path=":id" element={<ProductDetail />} /> {/* 匹配 "/products/123" */}
</Route>
</Route>
</Routes>
);
}
// 使用index路由实现默认标签页
function UserProfile() {
const { id } = useParams();
return (
<div className="user-profile">
<div className="profile-header">
<h1>User Profile</h1>
<nav className="profile-tabs">
<Link to={`/users/${id}`}>Overview</Link>
<Link to={`/users/${id}/posts`}>Posts</Link>
<Link to={`/users/${id}/followers`}>Followers</Link>
<Link to={`/users/${id}/settings`}>Settings</Link>
</nav>
</div>
<div className="profile-content">
<Outlet />
</div>
</div>
);
}
// 路由配置
<Route path="users/:id" element={<UserProfile />}>
<Route index element={<UserOverview />} /> {/* 默认显示概览 */}
<Route path="posts" element={<UserPosts />} />
<Route path="followers" element={<UserFollowers />} />
<Route path="settings" element={<UserSettings />} />
</Route>3. 相对路由
jsx
// 相对路径导航
function ProductsLayout() {
return (
<div className="products-layout">
<nav className="products-nav">
{/* 相对路径链接 */}
<Link to="." end>All Products</Link> {/* 当前路径 */}
<Link to="featured">Featured</Link> {/* 相对于当前路径 */}
<Link to="on-sale">On Sale</Link>
<Link to="../categories">Categories</Link> {/* 上级路径 */}
</nav>
<div className="products-content">
<Outlet />
</div>
</div>
);
}
// 编程式相对导航
function ProductCard({ product }) {
const navigate = useNavigate();
const handleViewDetails = () => {
navigate(product.id.toString()); // 相对导航到详情页
};
const handleEdit = () => {
navigate(`${product.id}/edit`); // 相对导航到编辑页
};
const handleBackToCategory = () => {
navigate(`../category/${product.category}`); // 导航到分类页
};
return (
<div className="product-card">
<h3>{product.name}</h3>
<p>${product.price}</p>
<div className="product-actions">
<button onClick={handleViewDetails}>View Details</button>
<button onClick={handleEdit}>Edit</button>
<button onClick={handleBackToCategory}>View Category</button>
</div>
</div>
);
}复杂嵌套路由案例
案例1:电商管理后台
jsx
import { Routes, Route, Outlet } from 'react-router-dom';
function EcommerceAdmin() {
return (
<Routes>
<Route path="/" element={<AdminLayout />}>
{/* 仪表板 */}
<Route index element={<Dashboard />} />
{/* 产品管理 */}
<Route path="products" element={<ProductsManagement />}>
<Route index element={<ProductsList />} />
<Route path="categories" element={<CategoriesManagement />}>
<Route index element={<CategoriesList />} />
<Route path=":categoryId" element={<CategoryDetail />} />
<Route path=":categoryId/edit" element={<EditCategory />} />
<Route path="new" element={<CreateCategory />} />
</Route>
<Route path="inventory" element={<InventoryManagement />}>
<Route index element={<InventoryOverview />} />
<Route path="low-stock" element={<LowStockProducts />} />
<Route path="transfers" element={<InventoryTransfers />} />
</Route>
<Route path=":productId" element={<ProductDetail />}>
<Route index element={<ProductOverview />} />
<Route path="variants" element={<ProductVariants />} />
<Route path="images" element={<ProductImages />} />
<Route path="seo" element={<ProductSEO />} />
<Route path="inventory" element={<ProductInventory />} />
</Route>
<Route path=":productId/edit" element={<EditProduct />} />
<Route path="new" element={<CreateProduct />} />
</Route>
{/* 订单管理 */}
<Route path="orders" element={<OrdersManagement />}>
<Route index element={<OrdersList />} />
<Route path="pending" element={<PendingOrders />} />
<Route path="processing" element={<ProcessingOrders />} />
<Route path="shipped" element={<ShippedOrders />} />
<Route path="returns" element={<Returns />} />
<Route path=":orderId" element={<OrderDetail />}>
<Route index element={<OrderOverview />} />
<Route path="items" element={<OrderItems />} />
<Route path="shipping" element={<OrderShipping />} />
<Route path="history" element={<OrderHistory />} />
</Route>
</Route>
{/* 客户管理 */}
<Route path="customers" element={<CustomersManagement />}>
<Route index element={<CustomersList />} />
<Route path="segments" element={<CustomerSegments />} />
<Route path=":customerId" element={<CustomerDetail />}>
<Route index element={<CustomerProfile />} />
<Route path="orders" element={<CustomerOrders />} />
<Route path="support" element={<CustomerSupport />} />
</Route>
</Route>
{/* 营销管理 */}
<Route path="marketing" element={<MarketingLayout />}>
<Route index element={<MarketingDashboard />} />
<Route path="campaigns" element={<CampaignsManagement />}>
<Route index element={<CampaignsList />} />
<Route path=":campaignId" element={<CampaignDetail />} />
<Route path="new" element={<CreateCampaign />} />
</Route>
<Route path="discounts" element={<DiscountsManagement />}>
<Route index element={<DiscountsList />} />
<Route path=":discountId" element={<DiscountDetail />} />
<Route path="new" element={<CreateDiscount />} />
</Route>
<Route path="email" element={<EmailMarketing />}>
<Route index element={<EmailCampaigns />} />
<Route path="templates" element={<EmailTemplates />} />
<Route path="subscribers" element={<EmailSubscribers />} />
</Route>
</Route>
{/* 设置 */}
<Route path="settings" element={<SettingsLayout />}>
<Route index element={<GeneralSettings />} />
<Route path="store" element={<StoreSettings />} />
<Route path="payments" element={<PaymentSettings />} />
<Route path="shipping" element={<ShippingSettings />} />
<Route path="taxes" element={<TaxSettings />} />
<Route path="users" element={<UserManagement />} />
</Route>
</Route>
{/* 认证页面(无布局) */}
<Route path="/login" element={<AdminLogin />} />
<Route path="*" element={<AdminNotFound />} />
</Routes>
);
}
// 布局组件
function AdminLayout() {
const location = useLocation();
return (
<div className="admin-layout">
<Sidebar currentPath={location.pathname} />
<div className="admin-main">
<Header />
<div className="admin-content">
<Outlet />
</div>
</div>
</div>
);
}
function ProductsManagement() {
return (
<div className="products-management">
<div className="management-header">
<h2>Products Management</h2>
<div className="header-actions">
<Link to="new" className="btn btn-primary">Add Product</Link>
<Link to="categories" className="btn">Manage Categories</Link>
<Link to="inventory" className="btn">Inventory</Link>
</div>
</div>
<div className="management-content">
<Outlet />
</div>
</div>
);
}
function ProductDetail() {
const { productId } = useParams();
const [product, setProduct] = useState(null);
useEffect(() => {
fetchProduct(productId).then(setProduct);
}, [productId]);
if (!product) return <div>Loading...</div>;
return (
<div className="product-detail">
<div className="product-header">
<h2>{product.name}</h2>
<div className="product-tabs">
<Link to={`/products/${productId}`} end>Overview</Link>
<Link to={`/products/${productId}/variants`}>Variants</Link>
<Link to={`/products/${productId}/images`}>Images</Link>
<Link to={`/products/${productId}/seo`}>SEO</Link>
<Link to={`/products/${productId}/inventory`}>Inventory</Link>
</div>
</div>
<div className="product-content">
<Outlet context={{ product, setProduct }} />
</div>
</div>
);
}案例2:学习管理系统
jsx
function LearningPlatform() {
return (
<Routes>
<Route path="/" element={<PlatformLayout />}>
{/* 首页 */}
<Route index element={<HomePage />} />
{/* 课程相关 */}
<Route path="courses" element={<CoursesLayout />}>
<Route index element={<CoursesCatalog />} />
<Route path="categories/:category" element={<CategoryCourses />} />
<Route path="search" element={<CoursesSearch />} />
<Route path=":courseId" element={<CourseDetail />}>
<Route index element={<CourseOverview />} />
<Route path="curriculum" element={<CourseCurriculum />} />
<Route path="reviews" element={<CourseReviews />} />
<Route path="instructor" element={<CourseInstructor />} />
</Route>
</Route>
{/* 学习路径 */}
<Route path="learning-paths" element={<LearningPathsLayout />}>
<Route index element={<LearningPathsList />} />
<Route path=":pathId" element={<LearningPathDetail />}>
<Route index element={<PathOverview />} />
<Route path="courses" element={<PathCourses />} />
<Route path="progress" element={<PathProgress />} />
</Route>
</Route>
{/* 我的学习 */}
<Route path="my-learning" element={<MyLearningLayout />}>
<Route index element={<MyLearningDashboard />} />
<Route path="enrolled" element={<EnrolledCourses />} />
<Route path="completed" element={<CompletedCourses />} />
<Route path="wishlist" element={<Wishlist />} />
<Route path="certificates" element={<Certificates />} />
<Route path="progress" element={<LearningProgress />} />
</Route>
{/* 课程学习界面 */}
<Route path="learn/:courseId" element={<LearningInterface />}>
<Route path="lesson/:lessonId" element={<LessonPlayer />} />
<Route path="quiz/:quizId" element={<Quiz />} />
<Route path="assignment/:assignmentId" element={<Assignment />} />
<Route path="discussion/:topicId?" element={<Discussion />} />
</Route>
{/* 用户相关 */}
<Route path="profile" element={<ProfileLayout />}>
<Route index element={<ProfileOverview />} />
<Route path="edit" element={<EditProfile />} />
<Route path="achievements" element={<Achievements />} />
<Route path="activity" element={<ActivityHistory />} />
</Route>
</Route>
{/* 认证页面 */}
<Route path="/auth" element={<AuthLayout />}>
<Route path="login" element={<Login />} />
<Route path="register" element={<Register />} />
<Route path="verify-email" element={<VerifyEmail />} />
</Route>
{/* 讲师管理 */}
<Route path="/instructor" element={<InstructorLayout />}>
<Route index element={<InstructorDashboard />} />
<Route path="courses" element={<InstructorCourses />}>
<Route index element={<CoursesList />} />
<Route path=":courseId" element={<ManageCourse />}>
<Route index element={<CourseSettings />} />
<Route path="content" element={<CourseContent />} />
<Route path="students" element={<CourseStudents />} />
<Route path="analytics" element={<CourseAnalytics />} />
</Route>
<Route path="new" element={<CreateCourse />} />
</Route>
</Route>
</Routes>
);
}
// 学习界面布局 - 全屏模式
function LearningInterface() {
const { courseId } = useParams();
const location = useLocation();
const [course, setCourse] = useState(null);
const [sidebarOpen, setSidebarOpen] = useState(true);
useEffect(() => {
fetchCourse(courseId).then(setCourse);
}, [courseId]);
if (!course) return <div>Loading course...</div>;
return (
<div className="learning-interface">
{/* 顶部导航条 */}
<header className="learning-header">
<div className="course-info">
<Link to={`/courses/${courseId}`} className="course-title">
{course.title}
</Link>
<span className="lesson-progress">
Lesson 3 of 12
</span>
</div>
<div className="learning-controls">
<button onClick={() => setSidebarOpen(!sidebarOpen)}>
Toggle Menu
</button>
<Link to="/my-learning">My Learning</Link>
</div>
</header>
<div className="learning-body">
{/* 侧边栏 - 课程内容导航 */}
{sidebarOpen && (
<aside className="course-sidebar">
<nav className="course-navigation">
<h3>Course Content</h3>
{course.modules.map(module => (
<div key={module.id} className="module">
<h4>{module.title}</h4>
<ul>
{module.lessons.map(lesson => (
<li key={lesson.id}>
<Link
to={`lesson/${lesson.id}`}
className={location.pathname.includes(`lesson/${lesson.id}`) ? 'active' : ''}
>
{lesson.title}
</Link>
</li>
))}
{module.quizzes.map(quiz => (
<li key={quiz.id}>
<Link to={`quiz/${quiz.id}`}>
Quiz: {quiz.title}
</Link>
</li>
))}
</ul>
</div>
))}
</nav>
</aside>
)}
{/* 主要学习内容 */}
<main className="learning-main">
<Outlet context={{ course, setCourse }} />
</main>
</div>
</div>
);
}案例3:多租户应用
jsx
function MultiTenantApp() {
return (
<Routes>
{/* 租户路由 */}
<Route path="/tenant/:tenantId" element={<TenantLayout />}>
<Route index element={<TenantDashboard />} />
{/* 租户的用户管理 */}
<Route path="users" element={<TenantUsersLayout />}>
<Route index element={<TenantUsersList />} />
<Route path="roles" element={<RolesManagement />} />
<Route path="permissions" element={<PermissionsManagement />} />
<Route path=":userId" element={<TenantUserDetail />}>
<Route index element={<UserProfile />} />
<Route path="permissions" element={<UserPermissions />} />
<Route path="activity" element={<UserActivity />} />
</Route>
</Route>
{/* 租户的应用设置 */}
<Route path="settings" element={<TenantSettingsLayout />}>
<Route index element={<GeneralSettings />} />
<Route path="branding" element={<BrandingSettings />} />
<Route path="integrations" element={<IntegrationsSettings />} />
<Route path="billing" element={<BillingSettings />} />
</Route>
</Route>
{/* 超级管理员路由 */}
<Route path="/admin" element={<SuperAdminLayout />}>
<Route index element={<SuperAdminDashboard />} />
<Route path="tenants" element={<TenantsManagement />}>
<Route index element={<TenantsList />} />
<Route path=":tenantId" element={<TenantDetail />} />
<Route path="new" element={<CreateTenant />} />
</Route>
<Route path="system" element={<SystemSettings />} />
</Route>
</Routes>
);
}
function TenantLayout() {
const { tenantId } = useParams();
const [tenant, setTenant] = useState(null);
useEffect(() => {
fetchTenant(tenantId).then(setTenant);
}, [tenantId]);
if (!tenant) return <div>Loading tenant...</div>;
return (
<div className="tenant-layout" data-tenant={tenantId}>
<header className="tenant-header">
<div className="tenant-brand">
<img src={tenant.logo} alt={tenant.name} />
<h1>{tenant.name}</h1>
</div>
<nav className="tenant-nav">
<Link to={`/tenant/${tenantId}`}>Dashboard</Link>
<Link to={`/tenant/${tenantId}/users`}>Users</Link>
<Link to={`/tenant/${tenantId}/settings`}>Settings</Link>
</nav>
</header>
<main className="tenant-main">
<Outlet context={{ tenant, setTenant }} />
</main>
</div>
);
}路由配置最佳实践
1. 路由结构设计
jsx
// 好的路由结构设计原则
const routeDesignPrinciples = {
// 1. 层次清晰
hierarchy: {
good: '/admin/products/123/edit',
explanation: '清晰的层次结构,便于理解'
},
// 2. 语义化
semantic: {
good: '/courses/javascript/lessons/variables',
bad: '/c/js/l/var',
explanation: '使用有意义的路径名称'
},
// 3. 一致性
consistency: {
good: ['/users/123', '/products/456', '/orders/789'],
bad: ['/user/123', '/product-detail/456', '/order_info/789'],
explanation: '保持命名和结构的一致性'
},
// 4. 可扩展性
scalability: {
good: {
current: '/api/v1/users',
future: '/api/v2/users'
},
explanation: '考虑未来的扩展需求'
}
};
// 实际应用
const wellDesignedRoutes = [
// 用户管理
{ path: '/users', name: 'Users List' },
{ path: '/users/:id', name: 'User Detail' },
{ path: '/users/:id/edit', name: 'Edit User' },
{ path: '/users/:id/orders', name: 'User Orders' },
// 产品管理
{ path: '/products', name: 'Products List' },
{ path: '/products/categories/:category', name: 'Category Products' },
{ path: '/products/:id', name: 'Product Detail' },
{ path: '/products/:id/variants', name: 'Product Variants' },
// 订单管理
{ path: '/orders', name: 'Orders List' },
{ path: '/orders/:id', name: 'Order Detail' },
{ path: '/orders/:id/tracking', name: 'Order Tracking' }
];2. 布局组件设计
jsx
// 可复用的布局组件
function createLayout(config) {
const {
showHeader = true,
showSidebar = true,
showFooter = true,
headerComponent = DefaultHeader,
sidebarComponent = DefaultSidebar,
footerComponent = DefaultFooter,
className = ''
} = config;
return function Layout() {
return (
<div className={`layout ${className}`}>
{showHeader && React.createElement(headerComponent)}
<div className="layout-body">
{showSidebar && (
<aside className="layout-sidebar">
{React.createElement(sidebarComponent)}
</aside>
)}
<main className="layout-main">
<Outlet />
</main>
</div>
{showFooter && React.createElement(footerComponent)}
</div>
);
};
}
// 创建不同的布局
const MainLayout = createLayout({
showHeader: true,
showSidebar: true,
showFooter: true,
className: 'main-layout'
});
const AuthLayout = createLayout({
showHeader: false,
showSidebar: false,
showFooter: false,
className: 'auth-layout'
});
const AdminLayout = createLayout({
headerComponent: AdminHeader,
sidebarComponent: AdminSidebar,
className: 'admin-layout'
});
// 响应式布局
function ResponsiveLayout() {
const [sidebarOpen, setSidebarOpen] = useState(true);
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768);
if (window.innerWidth < 768) {
setSidebarOpen(false);
}
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
return (
<div className={`responsive-layout ${sidebarOpen ? 'sidebar-open' : 'sidebar-closed'}`}>
<header className="layout-header">
<button
className="sidebar-toggle"
onClick={() => setSidebarOpen(!sidebarOpen)}
>
☰
</button>
<h1>My App</h1>
</header>
<div className="layout-body">
{(sidebarOpen || !isMobile) && (
<aside className="layout-sidebar">
<Navigation />
</aside>
)}
<main className="layout-main">
<Outlet />
</main>
</div>
</div>
);
}3. 路由守护和条件渲染
jsx
// 路由守护组件
function ProtectedRoute({ children, requiredPermission, fallback }) {
const { user, permissions } = useAuth();
const location = useLocation();
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
if (requiredPermission && !permissions.includes(requiredPermission)) {
return fallback || <AccessDenied />;
}
return children;
}
// 在路由配置中使用守护
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
{/* 受保护的路由 */}
<Route
path="dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
{/* 需要特定权限的路由 */}
<Route
path="admin/*"
element={
<ProtectedRoute
requiredPermission="admin:access"
fallback={<AccessDenied />}
>
<AdminRoutes />
</ProtectedRoute>
}
/>
{/* 角色基础的路由 */}
<Route
path="management/*"
element={
<RoleBasedRoute
allowedRoles={['admin', 'manager']}
fallback={<AccessDenied />}
>
<ManagementRoutes />
</RoleBasedRoute>
}
/>
</Route>
</Routes>
);
}
function RoleBasedRoute({ children, allowedRoles, fallback }) {
const { user } = useAuth();
if (!user || !allowedRoles.includes(user.role)) {
return fallback || <Navigate to="/" replace />;
}
return children;
}4. 路由状态管理
jsx
// 路由状态持久化
function useRoutePersistence() {
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
// 保存当前路由到sessionStorage
sessionStorage.setItem('lastRoute', location.pathname + location.search);
}, [location]);
const restoreLastRoute = useCallback(() => {
const lastRoute = sessionStorage.getItem('lastRoute');
if (lastRoute && lastRoute !== location.pathname) {
navigate(lastRoute);
}
}, [navigate, location]);
return { restoreLastRoute };
}
// 路由级数据预加载
function useRoutePreloader() {
const navigate = useNavigate();
const preloadRoute = useCallback(async (path) => {
try {
// 预加载路由对应的代码和数据
const routeData = await import(`./pages${path}`);
// 预取数据
if (routeData.preload) {
await routeData.preload();
}
console.log(`Route ${path} preloaded successfully`);
} catch (error) {
console.error(`Failed to preload route ${path}:`, error);
}
}, []);
const navigateWithPreload = useCallback(async (path, options = {}) => {
if (options.preload) {
await preloadRoute(path);
}
navigate(path, options);
}, [navigate, preloadRoute]);
return { preloadRoute, navigateWithPreload };
}
// 路由分析和追踪
function useRouteAnalytics() {
const location = useLocation();
const [routeHistory, setRouteHistory] = useState([]);
useEffect(() => {
const routeInfo = {
path: location.pathname,
search: location.search,
timestamp: Date.now(),
referrer: document.referrer
};
// 记录路由访问
setRouteHistory(prev => [...prev, routeInfo]);
// 发送分析数据
analytics.track('page_view', {
page: location.pathname,
title: document.title,
url: window.location.href
});
}, [location]);
const getRouteStats = useCallback(() => {
const stats = routeHistory.reduce((acc, route) => {
acc[route.path] = (acc[route.path] || 0) + 1;
return acc;
}, {});
return {
totalViews: routeHistory.length,
uniqueRoutes: Object.keys(stats).length,
mostVisited: Object.entries(stats)
.sort(([,a], [,b]) => b - a)
.slice(0, 5)
};
}, [routeHistory]);
return { routeHistory, getRouteStats };
}总结
React Router v6的路由配置和嵌套路由功能强大:
- 灵活的配置方式:JSX声明式和对象配置式
- 强大的嵌套支持:支持任意深度的路由嵌套
- 布局共享:通过Outlet实现布局组件复用
- 相对路径:简化路由间的导航
- 条件路由:支持基于权限和条件的路由渲染
合理的路由架构是构建大型React应用的基础。