react-router-dom中的几种路由详解

React Router DOM中的几种路由详解

1. React Router架构与核心概念

React Router是React生态系统中最核心的路由解决方案之一,为单页应用(SPA)提供声明式的路由管理。React Router DOM是专门为Web应用设计的路由库,基于History API实现客户端路由。

1.1 核心架构设计

graph TD A[React Router Core] --> B[History Management] A --> C[Route Matching] A --> D[Navigation Control] B --> E[BrowserRouter] B --> F[HashRouter] B --> G[MemoryRouter] C --> H[Route Component] C --> I[Routes Container] C --> J[Outlet Rendering] D --> K[Link Component] D --> L[NavLink Component] D --> M[useNavigate Hook]

1.2 核心概念解析

概念 作用 主要组件/Hook
Router Provider 路由上下文提供者 BrowserRouter, HashRouter
Route Matching 路由匹配与渲染 Route, Routes
Navigation 路由导航控制 Link, NavLink, useNavigate
Route State 路由状态管理 useLocation, useParams

1.3 版本演进与API变化

React Router经历了重大版本升级,v6版本引入了许多重要改进:

javascript 复制代码
// React Router v5 (Legacy)
import { Switch, Route, useHistory } from 'react-router-dom';

function AppV5() {
  const history = useHistory();
  
  return (
    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
    </Switch>
  );
}

// React Router v6 (Modern)
import { Routes, Route, useNavigate } from 'react-router-dom';

function AppV6() {
  const navigate = useNavigate();
  
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  );
}

2. 路由器类型详解

2.1 BrowserRouter - HTML5 History API路由器

BrowserRouter使用HTML5的History API来管理路由,提供干净的URL结构。

javascript 复制代码
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { createRoot } from 'react-dom/client';

function App() {
  return (
    <BrowserRouter>
      <div className="app">
        <Navigation />
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/products" element={<ProductsPage />} />
          <Route path="/products/:id" element={<ProductDetail />} />
          <Route path="/about" element={<AboutPage />} />
          <Route path="*" element={<NotFoundPage />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

// 高级配置示例
function AppWithConfig() {
  return (
    <BrowserRouter
      basename="/admin"  // 应用基础路径
      future={{
        v7_startTransition: true,  // 启用React 18并发特性
        v7_relativeSplatPath: true  // 启用相对路径特性
      }}
    >
      <Routes>
        {/* 路由配置 */}
      </Routes>
    </BrowserRouter>
  );
}

const root = createRoot(document.getElementById('root'));
root.render(<App />);

BrowserRouter特点:

  • URL结构: https://example.com/products/123
  • SEO友好,URL结构清晰
  • 需要服务器配置支持,处理直接访问和刷新
  • 支持浏览器前进后退按钮

2.2 HashRouter - Hash模式路由器

HashRouter使用URL的hash部分来管理路由,无需服务器端配置。

javascript 复制代码
import { HashRouter, Routes, Route } from 'react-router-dom';

function HashApp() {
  return (
    <HashRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </HashRouter>
  );
}

// 适用场景:静态文件部署
function StaticDeploymentApp() {
  return (
    <HashRouter
      hashType="slash"  // URL格式: #/path
      // hashType="noslash" // URL格式: #path
    >
      <Routes>
        <Route path="/" element={<StaticHome />} />
        <Route path="/docs" element={<Documentation />} />
      </Routes>
    </HashRouter>
  );
}

HashRouter特点:

  • URL结构: https://example.com/#/products/123
  • 无需服务器配置,适合静态部署
  • SEO支持有限
  • 兼容性更好,支持老旧浏览器

2.3 MemoryRouter - 内存路由器

MemoryRouter将路由历史记录保存在内存中,适用于测试环境和非浏览器环境。

javascript 复制代码
import { MemoryRouter, Routes, Route } from 'react-router-dom';

// 测试环境使用
function TestApp({ initialEntries = ['/'] }) {
  return (
    <MemoryRouter 
      initialEntries={initialEntries}
      initialIndex={0}
    >
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/login" element={<Login />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </MemoryRouter>
  );
}

// React Native应用中使用
function MobileApp() {
  return (
    <MemoryRouter>
      <Routes>
        <Route path="/" element={<MobileHome />} />
        <Route path="/profile" element={<Profile />} />
      </Routes>
    </MemoryRouter>
  );
}

2.4 路由器选择决策流程

flowchart TD A[选择路由器类型] --> B{部署环境?} B -->|Web应用| C{服务器配置?} B -->|移动端/测试| H[MemoryRouter] C -->|支持History API| D[BrowserRouter] C -->|静态文件部署| E[HashRouter] D --> F{SEO需求?} E --> G{URL美观度?} F -->|重要| I[推荐BrowserRouter + 服务器配置] F -->|一般| J[BrowserRouter可选] G -->|重要| K[考虑BrowserRouter] G -->|一般| L[HashRouter适合]

3. Route组件深度解析

3.1 基础路由配置

javascript 复制代码
import { Routes, Route } from 'react-router-dom';

function BasicRouting() {
  return (
    <Routes>
      {/* 精确匹配根路径 */}
      <Route path="/" element={<HomePage />} />
      
      {/* 动态参数路由 */}
      <Route path="/user/:id" element={<UserProfile />} />
      
      {/* 可选参数路由 */}
      <Route path="/posts/:id?" element={<PostList />} />
      
      {/* 通配符路由 */}
      <Route path="/files/*" element={<FileExplorer />} />
      
      {/* 404路由 - 必须放在最后 */}
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}

3.2 嵌套路由架构

嵌套路由是React Router的强大特性,允许构建复杂的应用结构。

javascript 复制代码
import { Routes, Route, Outlet, useParams } from 'react-router-dom';

// 主路由配置
function App() {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />
        <Route path="about" element={<About />} />
        
        {/* 嵌套路由:用户管理 */}
        <Route path="users" element={<UsersLayout />}>
          <Route index element={<UsersList />} />
          <Route path=":id" element={<UserDetail />} />
          <Route path=":id/edit" element={<UserEdit />} />
          <Route path="new" element={<UserCreate />} />
        </Route>
        
        {/* 嵌套路由:产品管理 */}
        <Route path="products/*" element={<ProductsApp />} />
      </Route>
    </Routes>
  );
}

// 布局组件
function Layout() {
  return (
    <div className="app-layout">
      <Header />
      <nav>
        <Link to="/">首页</Link>
        <Link to="/about">关于</Link>
        <Link to="/users">用户</Link>
        <Link to="/products">产品</Link>
      </nav>
      <main>
        <Outlet /> {/* 嵌套路由渲染位置 */}
      </main>
      <Footer />
    </div>
  );
}

// 用户模块布局
function UsersLayout() {
  return (
    <div className="users-layout">
      <aside>
        <h2>用户管理</h2>
        <nav>
          <Link to="/users">用户列表</Link>
          <Link to="/users/new">新建用户</Link>
        </nav>
      </aside>
      <section>
        <Outlet /> {/* 用户子路由渲染 */}
      </section>
    </div>
  );
}

// 产品模块 - 独立路由配置
function ProductsApp() {
  return (
    <Routes>
      <Route index element={<ProductsList />} />
      <Route path="category/:category" element={<CategoryProducts />} />
      <Route path=":id" element={<ProductDetail />} />
      <Route path=":id/reviews" element={<ProductReviews />} />
    </Routes>
  );
}

3.3 动态路由与参数提取

javascript 复制代码
import { useParams, useSearchParams, useLocation } from 'react-router-dom';

// 路径参数提取
function UserProfile() {
  const { id, tab } = useParams(); // 从 /user/:id/:tab 提取
  
  return (
    <div>
      <h1>用户 #{id}</h1>
      <UserTabs activeTab={tab} />
    </div>
  );
}

// 查询参数处理
function ProductsList() {
  const [searchParams, setSearchParams] = useSearchParams();
  
  const category = searchParams.get('category');
  const sort = searchParams.get('sort') || 'name';
  const page = parseInt(searchParams.get('page') || '1');
  
  const updateFilter = (key, value) => {
    setSearchParams(prev => {
      const newParams = new URLSearchParams(prev);
      if (value) {
        newParams.set(key, value);
      } else {
        newParams.delete(key);
      }
      return newParams;
    });
  };
  
  return (
    <div>
      <ProductFilter 
        category={category}
        sort={sort}
        onFilterChange={updateFilter}
      />
      <ProductGrid 
        products={getProducts({ category, sort, page })}
      />
      <Pagination 
        current={page}
        total={getTotalPages()}
        onChange={(page) => updateFilter('page', page)}
      />
    </div>
  );
}

// 位置信息获取
function LocationInfo() {
  const location = useLocation();
  
  return (
    <div>
      <p>当前路径: {location.pathname}</p>
      <p>查询参数: {location.search}</p>
      <p>Hash: {location.hash}</p>
      <p>State: {JSON.stringify(location.state)}</p>
    </div>
  );
}

3.4 条件路由与权限控制

javascript 复制代码
import { Navigate, useLocation } from 'react-router-dom';

// 权限路由组件
function ProtectedRoute({ children, requiredRole }) {
  const { user, isAuthenticated } = useAuth();
  const location = useLocation();
  
  if (!isAuthenticated) {
    // 重定向到登录页,并保存当前位置
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  
  if (requiredRole && !user.roles.includes(requiredRole)) {
    return <Navigate to="/unauthorized" replace />;
  }
  
  return children;
}

// 使用权限路由
function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route path="/unauthorized" element={<Unauthorized />} />
      
      {/* 需要登录的路由 */}
      <Route 
        path="/dashboard" 
        element={
          <ProtectedRoute>
            <Dashboard />
          </ProtectedRoute>
        } 
      />
      
      {/* 需要管理员权限的路由 */}
      <Route 
        path="/admin/*" 
        element={
          <ProtectedRoute requiredRole="admin">
            <AdminPanel />
          </ProtectedRoute>
        } 
      />
      
      {/* 条件重定向 */}
      <Route 
        path="/profile" 
        element={
          user ? <UserProfile /> : <Navigate to="/login" />
        } 
      />
    </Routes>
  );
}

4. 路由导航与状态管理

4.1 导航组件详解

javascript 复制代码
import { Link, NavLink, useNavigate } from 'react-router-dom';

// 基础导航链接
function BasicNavigation() {
  return (
    <nav>
      {/* 基础链接 */}
      <Link to="/">首页</Link>
      <Link to="/about">关于我们</Link>
      
      {/* 相对路径链接 */}
      <Link to="../parent">上级目录</Link>
      <Link to="./child">子目录</Link>
      
      {/* 带查询参数的链接 */}
      <Link to="/search?q=react&type=tutorial">搜索</Link>
      
      {/* 带状态的链接 */}
      <Link 
        to="/detail" 
        state={{ from: 'navigation', data: { id: 1 } }}
      >
        详情页
      </Link>
    </nav>
  );
}

// 活跃状态导航
function ActiveNavigation() {
  return (
    <nav className="main-nav">
      <NavLink 
        to="/"
        className={({ isActive, isPending }) => 
          isActive ? 'nav-link active' : 'nav-link'
        }
        style={({ isActive }) => ({
          color: isActive ? '#ff0000' : '#000000'
        })}
      >
        首页
      </NavLink>
      
      <NavLink 
        to="/products"
        className="nav-link"
        end // 精确匹配,不匹配子路由
      >
        产品
      </NavLink>
      
      {/* 自定义活跃判断 */}
      <NavLink
        to="/blog"
        className={({ isActive }) => {
          // 自定义逻辑判断是否活跃
          return `nav-link ${isActive ? 'active' : ''}`;
        }}
      >
        博客
      </NavLink>
    </nav>
  );
}

4.2 程序化导航

javascript 复制代码
import { useNavigate, useLocation } from 'react-router-dom';

function ProgrammaticNavigation() {
  const navigate = useNavigate();
  const location = useLocation();
  
  // 基础导航
  const goToHome = () => {
    navigate('/');
  };
  
  // 带参数导航
  const goToUser = (userId) => {
    navigate(`/user/${userId}`);
  };
  
  // 查询参数导航
  const searchProducts = (query) => {
    navigate(`/products?search=${encodeURIComponent(query)}`);
  };
  
  // 替换当前历史记录
  const replaceToLogin = () => {
    navigate('/login', { replace: true });
  };
  
  // 带状态导航
  const goToDetailWithState = (product) => {
    navigate('/product-detail', {
      state: { 
        product,
        returnTo: location.pathname 
      }
    });
  };
  
  // 历史记录导航
  const goBack = () => {
    navigate(-1); // 后退一步
  };
  
  const goForward = () => {
    navigate(1); // 前进一步
  };
  
  // 相对导航
  const goToRelative = () => {
    navigate('../sibling'); // 相对于当前路径
    navigate('.', { replace: true }); // 刷新当前页面
  };
  
  return (
    <div>
      <button onClick={goToHome}>首页</button>
      <button onClick={() => goToUser(123)}>用户详情</button>
      <button onClick={() => searchProducts('laptop')}>搜索产品</button>
      
      <button onClick={goBack}>返回</button>
      <button onClick={goForward}>前进</button>
      
      <button onClick={replaceToLogin}>去登录</button>
    </div>
  );
}

4.3 路由导航流程图

sequenceDiagram participant User as 用户操作 participant Router as React Router participant History as History API participant Component as 组件系统 User->>Router: 点击Link/调用navigate Router->>History: 更新浏览器历史 History->>Router: 触发location变化 Router->>Router: 执行路由匹配 alt 路由匹配成功 Router->>Component: 渲染匹配的组件 Component->>User: 显示新页面内容 else 路由不匹配 Router->>Component: 渲染404或重定向 Component->>User: 显示错误页面 end Note over Router: 路由守卫检查 Note over Component: 组件生命周期执行

5. 高级路由特性

5.1 路由懒加载与代码分割

javascript 复制代码
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

// 懒加载组件定义
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const ProductsPage = lazy(() => import('./pages/ProductsPage'));
const UserDashboard = lazy(() => 
  import('./pages/UserDashboard').then(module => ({
    default: module.UserDashboard
  }))
);

// 自定义加载组件
function RouteLoadingSpinner() {
  return (
    <div className="route-loading">
      <div className="spinner"></div>
      <p>页面加载中...</p>
    </div>
  );
}

// 错误边界组件
class RouteErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    console.error('路由加载错误:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <div className="route-error">
          <h2>页面加载失败</h2>
          <button onClick={() => window.location.reload()}>
            重新加载
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

// 路由配置
function App() {
  return (
    <Routes>
      <Route 
        path="/" 
        element={
          <RouteErrorBoundary>
            <Suspense fallback={<RouteLoadingSpinner />}>
              <HomePage />
            </Suspense>
          </RouteErrorBoundary>
        } 
      />
      
      <Route 
        path="/about" 
        element={
          <Suspense fallback={<RouteLoadingSpinner />}>
            <AboutPage />
          </Suspense>
        } 
      />
      
      {/* 产品模块 - 整个模块懒加载 */}
      <Route 
        path="/products/*" 
        element={
          <Suspense fallback={<RouteLoadingSpinner />}>
            <ProductsPage />
          </Suspense>
        } 
      />
    </Routes>
  );
}

5.2 路由守卫实现

javascript 复制代码
import { useEffect, useState } from 'react';
import { Navigate, useLocation } from 'react-router-dom';

// 路由守卫Hook
function useRouteGuard(guardConfig) {
  const [guardResult, setGuardResult] = useState(null);
  const location = useLocation();
  
  useEffect(() => {
    const executeGuards = async () => {
      try {
        // 执行前置守卫
        if (guardConfig.beforeEnter) {
          const result = await guardConfig.beforeEnter(location);
          if (result === false) {
            setGuardResult({ type: 'block' });
            return;
          }
          if (typeof result === 'string') {
            setGuardResult({ type: 'redirect', to: result });
            return;
          }
        }
        
        // 权限检查
        if (guardConfig.requireAuth && !guardConfig.isAuthenticated()) {
          setGuardResult({ 
            type: 'redirect', 
            to: '/login',
            state: { from: location }
          });
          return;
        }
        
        // 角色检查
        if (guardConfig.requiredRoles) {
          const userRoles = guardConfig.getUserRoles();
          const hasRole = guardConfig.requiredRoles.some(role => 
            userRoles.includes(role)
          );
          if (!hasRole) {
            setGuardResult({ type: 'redirect', to: '/unauthorized' });
            return;
          }
        }
        
        setGuardResult({ type: 'allow' });
      } catch (error) {
        console.error('路由守卫执行错误:', error);
        setGuardResult({ type: 'redirect', to: '/error' });
      }
    };
    
    executeGuards();
  }, [location, guardConfig]);
  
  return guardResult;
}

// 守卫组件
function GuardedRoute({ children, ...guardConfig }) {
  const guardResult = useRouteGuard(guardConfig);
  
  if (!guardResult) {
    return <div>检查权限中...</div>;
  }
  
  switch (guardResult.type) {
    case 'allow':
      return children;
    case 'redirect':
      return <Navigate 
        to={guardResult.to} 
        state={guardResult.state}
        replace 
      />;
    case 'block':
      return <div>访问被阻止</div>;
    default:
      return <div>未知守卫状态</div>;
  }
}

// 使用守卫的路由配置
function App() {
  const authService = useAuthService();
  
  return (
    <Routes>
      {/* 公开路由 */}
      <Route path="/login" element={<Login />} />
      <Route path="/register" element={<Register />} />
      
      {/* 需要登录的路由 */}
      <Route 
        path="/dashboard" 
        element={
          <GuardedRoute
            requireAuth={true}
            isAuthenticated={() => authService.isLoggedIn()}
            beforeEnter={async (location) => {
              // 异步权限检查
              const hasAccess = await authService.checkAccess('/dashboard');
              return hasAccess;
            }}
          >
            <Dashboard />
          </GuardedRoute>
        } 
      />
      
      {/* 管理员路由 */}
      <Route 
        path="/admin/*" 
        element={
          <GuardedRoute
            requireAuth={true}
            requiredRoles={['admin', 'super-admin']}
            isAuthenticated={() => authService.isLoggedIn()}
            getUserRoles={() => authService.getUserRoles()}
          >
            <AdminPanel />
          </GuardedRoute>
        } 
      />
    </Routes>
  );
}

5.3 路由数据预加载

javascript 复制代码
import { useEffect, useState } from 'react';
import { useParams, useLoaderData } from 'react-router-dom';

// 路由数据加载器
export const productLoader = async ({ params }) => {
  const { id } = params;
  
  try {
    const [product, reviews, recommendations] = await Promise.all([
      fetchProduct(id),
      fetchProductReviews(id),
      fetchRecommendations(id)
    ]);
    
    return {
      product,
      reviews,
      recommendations
    };
  } catch (error) {
    throw new Response('产品不存在', { status: 404 });
  }
};

// 使用预加载数据的组件
function ProductDetail() {
  const { product, reviews, recommendations } = useLoaderData();
  const [loading, setLoading] = useState(false);
  
  return (
    <div className="product-detail">
      <ProductInfo product={product} />
      <ProductReviews reviews={reviews} />
      <Recommendations items={recommendations} />
    </div>
  );
}

// 自定义数据预加载Hook
function useRouteData(dataLoader, deps = []) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    let cancelled = false;
    
    const loadData = async () => {
      try {
        setLoading(true);
        setError(null);
        
        const result = await dataLoader();
        
        if (!cancelled) {
          setData(result);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    };
    
    loadData();
    
    return () => {
      cancelled = true;
    };
  }, deps);
  
  return { data, loading, error };
}

// 使用自定义预加载Hook
function UserProfile() {
  const { id } = useParams();
  
  const { data: user, loading, error } = useRouteData(
    () => fetchUserProfile(id),
    [id]
  );
  
  if (loading) return <UserProfileSkeleton />;
  if (error) return <ErrorMessage error={error} />;
  if (!user) return <UserNotFound />;
  
  return <UserProfileContent user={user} />;
}

6. 性能优化与最佳实践

6.1 路由性能优化策略

javascript 复制代码
import { memo, useMemo, useCallback } from 'react';
import { useLocation, matchPath } from 'react-router-dom';

// 路由缓存优化
const RouteCache = new Map();

function useCachedRoute(routeKey, routeComponent) {
  return useMemo(() => {
    if (!RouteCache.has(routeKey)) {
      RouteCache.set(routeKey, routeComponent);
    }
    return RouteCache.get(routeKey);
  }, [routeKey]);
}

// 智能路由匹配
function useSmartRouteMatch(patterns) {
  const location = useLocation();
  
  return useMemo(() => {
    for (const pattern of patterns) {
      const match = matchPath(pattern, location.pathname);
      if (match) {
        return { pattern, match };
      }
    }
    return null;
  }, [location.pathname, patterns]);
}

// 性能优化的导航组件
const OptimizedNavigation = memo(function Navigation({ items }) {
  const location = useLocation();
  
  const navItems = useMemo(() => {
    return items.map(item => ({
      ...item,
      isActive: location.pathname === item.path
    }));
  }, [items, location.pathname]);
  
  const handleNavClick = useCallback((path) => {
    // 预加载目标页面资源
    if (path !== location.pathname) {
      preloadRoute(path);
    }
  }, [location.pathname]);
  
  return (
    <nav>
      {navItems.map(item => (
        <NavLink
          key={item.path}
          to={item.path}
          className={item.isActive ? 'active' : ''}
          onClick={() => handleNavClick(item.path)}
        >
          {item.label}
        </NavLink>
      ))}
    </nav>
  );
});

// 路由预加载
function preloadRoute(path) {
  const routeMap = {
    '/products': () => import('./pages/ProductsPage'),
    '/about': () => import('./pages/AboutPage'),
    '/contact': () => import('./pages/ContactPage')
  };
  
  const preloader = routeMap[path];
  if (preloader) {
    // 在空闲时预加载
    if ('requestIdleCallback' in window) {
      requestIdleCallback(() => preloader());
    } else {
      setTimeout(preloader, 100);
    }
  }
}

6.2 路由状态管理

javascript 复制代码
import { createContext, useContext, useReducer } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';

// 路由状态上下文
const RouteStateContext = createContext();

// 路由状态管理器
function routeStateReducer(state, action) {
  switch (action.type) {
    case 'SET_BREADCRUMB':
      return {
        ...state,
        breadcrumb: action.payload
      };
    
    case 'SET_PAGE_TITLE':
      return {
        ...state,
        pageTitle: action.payload
      };
    
    case 'SET_NAVIGATION_STATE':
      return {
        ...state,
        navigation: {
          ...state.navigation,
          ...action.payload
        }
      };
    
    case 'ADD_ROUTE_HISTORY':
      return {
        ...state,
        routeHistory: [
          ...state.routeHistory.slice(-9), // 保留最近10条记录
          action.payload
        ]
      };
    
    default:
      return state;
  }
}

// 路由状态提供者
function RouteStateProvider({ children }) {
  const [state, dispatch] = useReducer(routeStateReducer, {
    breadcrumb: [],
    pageTitle: '',
    navigation: {
      isCollapsed: false,
      activeMenu: null
    },
    routeHistory: []
  });
  
  const location = useLocation();
  
  useEffect(() => {
    // 记录路由历史
    dispatch({
      type: 'ADD_ROUTE_HISTORY',
      payload: {
        path: location.pathname,
        search: location.search,
        timestamp: Date.now()
      }
    });
  }, [location]);
  
  return (
    <RouteStateContext.Provider value={{ state, dispatch }}>
      {children}
    </RouteStateContext.Provider>
  );
}

// 路由状态Hook
function useRouteState() {
  const context = useContext(RouteStateContext);
  if (!context) {
    throw new Error('useRouteState must be used within RouteStateProvider');
  }
  return context;
}

// 面包屑导航Hook
function useBreadcrumb() {
  const { state, dispatch } = useRouteState();
  const location = useLocation();
  
  const setBreadcrumb = useCallback((breadcrumb) => {
    dispatch({ type: 'SET_BREADCRUMB', payload: breadcrumb });
  }, [dispatch]);
  
  // 自动生成面包屑
  const generateBreadcrumb = useCallback(() => {
    const pathSegments = location.pathname.split('/').filter(Boolean);
    const breadcrumb = pathSegments.map((segment, index) => {
      const path = '/' + pathSegments.slice(0, index + 1).join('/');
      return {
        label: segment.charAt(0).toUpperCase() + segment.slice(1),
        path: path,
        isActive: index === pathSegments.length - 1
      };
    });
    
    setBreadcrumb([{ label: '首页', path: '/', isActive: false }, ...breadcrumb]);
  }, [location.pathname, setBreadcrumb]);
  
  return {
    breadcrumb: state.breadcrumb,
    setBreadcrumb,
    generateBreadcrumb
  };
}

6.3 路由监控与分析

javascript 复制代码
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

// 路由性能监控Hook
function useRouteAnalytics() {
  const location = useLocation();
  
  useEffect(() => {
    const startTime = performance.now();
    
    // 页面视图统计
    if (window.gtag) {
      window.gtag('config', 'GA_MEASUREMENT_ID', {
        page_path: location.pathname + location.search
      });
    }
    
    // 自定义分析
    const routeData = {
      path: location.pathname,
      search: location.search,
      hash: location.hash,
      timestamp: Date.now(),
      userAgent: navigator.userAgent,
      referrer: document.referrer
    };
    
    // 发送路由访问数据
    fetch('/api/analytics/route', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(routeData)
    }).catch(error => {
      console.warn('路由分析数据发送失败:', error);
    });
    
    // 性能监控
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      entries.forEach(entry => {
        if (entry.entryType === 'navigation') {
          // 记录页面加载性能
          const performanceData = {
            path: location.pathname,
            loadTime: entry.loadEventEnd - entry.loadEventStart,
            domContentLoaded: entry.domContentLoadedEventEnd - entry.domContentLoadedEventStart,
            firstPaint: entry.responseEnd - entry.requestStart
          };
          
          fetch('/api/analytics/performance', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(performanceData)
          });
        }
      });
    });
    
    observer.observe({ entryTypes: ['navigation'] });
    
    return () => {
      observer.disconnect();
      
      // 记录页面停留时间
      const endTime = performance.now();
      const stayTime = endTime - startTime;
      
      if (stayTime > 1000) { // 停留超过1秒才记录
        fetch('/api/analytics/stay-time', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            path: location.pathname,
            stayTime: Math.round(stayTime)
          })
        });
      }
    };
  }, [location]);
}

// 在应用中使用监控
function App() {
  useRouteAnalytics();
  
  return (
    <RouteStateProvider>
      <BrowserRouter>
        <Routes>
          {/* 路由配置 */}
        </Routes>
      </BrowserRouter>
    </RouteStateProvider>
  );
}

7. 测试与调试

7.1 路由组件测试

javascript 复制代码
import { render, screen, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { vi } from 'vitest';

// 路由组件测试工具
function renderWithRouter(component, { initialEntries = ['/'] } = {}) {
  return render(
    <MemoryRouter initialEntries={initialEntries}>
      {component}
    </MemoryRouter>
  );
}

// 路由导航测试
describe('Navigation Component', () => {
  test('应该正确渲染导航链接', () => {
    renderWithRouter(<Navigation />);
    
    expect(screen.getByText('首页')).toBeInTheDocument();
    expect(screen.getByText('产品')).toBeInTheDocument();
    expect(screen.getByText('关于')).toBeInTheDocument();
  });
  
  test('应该正确处理链接点击', () => {
    const { container } = renderWithRouter(<App />, {
      initialEntries: ['/']
    });
    
    const productLink = screen.getByText('产品');
    fireEvent.click(productLink);
    
    expect(screen.getByText('产品列表页面')).toBeInTheDocument();
  });
  
  test('应该正确处理404页面', () => {
    renderWithRouter(<App />, {
      initialEntries: ['/non-existent-page']
    });
    
    expect(screen.getByText('页面未找到')).toBeInTheDocument();
  });
});

// 路由守卫测试
describe('Protected Routes', () => {
  test('未登录用户应该被重定向到登录页', () => {
    const mockAuthService = {
      isLoggedIn: () => false,
      getUserRoles: () => []
    };
    
    renderWithRouter(
      <AuthProvider value={mockAuthService}>
        <App />
      </AuthProvider>,
      { initialEntries: ['/dashboard'] }
    );
    
    expect(screen.getByText('登录页面')).toBeInTheDocument();
  });
  
  test('已登录用户应该能访问受保护页面', () => {
    const mockAuthService = {
      isLoggedIn: () => true,
      getUserRoles: () => ['user']
    };
    
    renderWithRouter(
      <AuthProvider value={mockAuthService}>
        <App />
      </AuthProvider>,
      { initialEntries: ['/dashboard'] }
    );
    
    expect(screen.getByText('用户仪表板')).toBeInTheDocument();
  });
});

7.2 路由调试工具

javascript 复制代码
import { useEffect } from 'react';
import { useLocation, useNavigationType } from 'react-router-dom';

// 路由调试Hook
function useRouteDebugger(enabled = process.env.NODE_ENV === 'development') {
  const location = useLocation();
  const navigationType = useNavigationType();
  
  useEffect(() => {
    if (!enabled) return;
    
    const routeInfo = {
      pathname: location.pathname,
      search: location.search,
      hash: location.hash,
      state: location.state,
      navigationType: navigationType,
      timestamp: new Date().toISOString()
    };
    
    console.group('🛣️ Route Change');
    console.log('Location:', routeInfo);
    console.log('Full URL:', window.location.href);
    console.groupEnd();
    
    // 在控制台暴露路由信息
    window.__ROUTE_DEBUG__ = {
      ...routeInfo,
      history: window.history,
      back: () => window.history.back(),
      forward: () => window.history.forward(),
      go: (delta) => window.history.go(delta)
    };
  }, [location, navigationType, enabled]);
}

// 路由开发工具组件
function RouteDevTools() {
  const location = useLocation();
  const [isOpen, setIsOpen] = useState(false);
  
  if (process.env.NODE_ENV !== 'development') {
    return null;
  }
  
  return (
    <div className="route-devtools">
      <button
        className="devtools-toggle"
        onClick={() => setIsOpen(!isOpen)}
      >
        🛣️
      </button>
      
      {isOpen && (
        <div className="devtools-panel">
          <h3>路由信息</h3>
          <div>
            <strong>路径:</strong> {location.pathname}
          </div>
          <div>
            <strong>查询:</strong> {location.search || '无'}
          </div>
          <div>
            <strong>Hash:</strong> {location.hash || '无'}
          </div>
          <div>
            <strong>状态:</strong> 
            <pre>{JSON.stringify(location.state, null, 2)}</pre>
          </div>
          
          <h4>快速导航</h4>
          <div className="quick-nav">
            <button onClick={() => window.history.back()}>
              ← 后退
            </button>
            <button onClick={() => window.history.forward()}>
              前进 →
            </button>
            <button onClick={() => window.location.reload()}>
              🔄 刷新
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

8. 总结与最佳实践

8.1 核心价值总结

React Router DOM为React应用提供了强大而灵活的路由解决方案:

  1. 声明式路由配置: 通过JSX组件的方式配置路由,直观易懂
  2. 嵌套路由支持: 支持复杂的嵌套路由结构,适合大型应用
  3. 动态路由匹配: 灵活的参数提取和路径匹配机制
  4. 程序化导航: 提供丰富的API进行程序控制导航

8.2 路由选择决策指南

  • BrowserRouter: 适用于需要SEO优化的现代Web应用
  • HashRouter: 适用于静态部署或需要兼容老旧浏览器的场景
  • MemoryRouter: 适用于测试环境或非浏览器环境

8.3 性能优化建议

  • 懒加载: 使用React.lazy()和Suspense实现路由级别的代码分割
  • 预加载: 在用户交互时预加载可能访问的路由组件
  • 缓存策略: 合理使用useMemo和useCallback优化路由组件渲染
  • 监控分析: 建立路由性能监控体系,及时发现和解决问题

8.4 开发最佳实践

  • 统一路由管理: 建立统一的路由配置和管理机制
  • 权限控制: 实现完善的路由守卫和权限检查
  • 错误处理: 添加适当的错误边界和404页面处理
  • 测试覆盖: 为路由功能编写充分的单元测试和集成测试

通过合理运用React Router DOM的各种特性,我们能够构建出结构清晰、性能优良、用户体验优秀的单页应用。在实际项目中,应根据具体需求选择合适的路由策略,并结合现代前端工程化实践,打造高质量的路由系统。

相关推荐
Benzenene!1 分钟前
让Chrome信任自签名证书
前端·chrome
yangholmes88881 分钟前
如何在 web 应用中使用 GDAL (二)
前端·webassembly
jacy3 分钟前
图片大图预览就该这样做
前端
林太白5 分钟前
Nuxt3 功能篇
前端·javascript·后端
YuJie7 分钟前
webSocket Manager
前端·javascript
Mapmost22 分钟前
Mapmost SDK for UE5 内核升级,三维场景渲染效果飙升!
前端
Mapmost25 分钟前
重磅升级丨Mapmost全面兼容3DTiles 1.1,3DGS量测精度跃升至亚米级!
前端·vue.js·three.js
wycode31 分钟前
Promise(一)极简版demo
前端·javascript
浮幻云月32 分钟前
一个自开自用的Ai提效VsCode插件
前端·javascript