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

相关推荐
唐某人丶2 分钟前
前端仔如何在公司搭建 AI Review 系统
前端·人工智能·aigc
没有鸡汤吃不下饭2 分钟前
排查vue项目线上才会出现的故障
前端·vue.js·nginx
吃饭睡觉打豆豆嘛4 分钟前
React Router 传参三板斧:新手也能 5 秒做决定
前端
裘乡6 分钟前
storybook配合vite + react生成组件文档
前端·react.js
Carolinemy7 分钟前
ElementUI 之 el-table
前端·vue.js
裘乡10 分钟前
vonage音视频基本使用--web@opentok/client
前端·音视频开发
BugCollect13 分钟前
Lodash常用方法
前端·javascript
轻语呢喃29 分钟前
TypeScript:从类型安全到高效开发
react.js·typescript
低代码布道师31 分钟前
HTML5 `<figure>` 标签:提升网页语义化与可访问性的利器
前端·html·html5
CAD老兵33 分钟前
解锁 JavaScript 模块的秘密:ES6模块内部结构详解
前端·javascript