嵌套路由

2025年11月29日 作者:管理员

示例代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>React Router 嵌套路由</title>
  
  <!-- 引入 Tailwind CSS 进行快速样式设计 -->
  <script src="https://cdn.tailwindcss.com/3.4.17"></script>
  
  <!-- 1. React 和 ReactDOM (Production 版本) -->
  <script crossorigin src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
  
  <!-- 2. React Router 依赖链 (顺序非常重要,Production 版本) -->
  <!-- 2.1 @remix-run/router: React Router 6.4+ 的底层路由逻辑 -->
  <script src="https://unpkg.com/@remix-run/router@1.15.3/dist/router.umd.min.js"></script>
  
  <!-- 2.2 react-router: 核心 React 绑定 -->
  <script src="https://unpkg.com/react-router@6.22.3/dist/umd/react-router.production.min.js"></script>
  
  <!-- 2.3 react-router-dom: DOM 绑定 (我们需要使用的对象在这里) -->
  <script src="https://unpkg.com/react-router-dom@6.22.3/dist/umd/react-router-dom.production.min.js"></script>
  
  <!-- Babel 用于解析 JSX -->
  <script src="https://unpkg.com/@babel/standalone@7.28.5/babel.min.js"></script>

  <style>
    .page-enter {
      animation: fade-in 0.3s ease-out;
    }
    @keyframes fade-in {
      from { opacity: 0; transform: translateY(10px); }
      to { opacity: 1; transform: translateY(0); }
    }
  </style>
</head>
<body class="bg-gray-50 text-gray-800 min-h-screen">
  <div id="root"></div>

  <script type="text/babel">
    const { HashRouter, Routes, Route, Link, Outlet, useParams } = window.ReactRouterDOM;

    function Layout() {
      return (
        <div className="min-h-screen flex flex-col">
          <nav className="bg-white shadow-sm border-b border-gray-200 mb-8 sticky top-0 z-10">
            <div className="max-w-4xl mx-auto px-4 py-4">
              <div className="flex gap-4">
                <Link 
                  to="/" 
                  className="px-4 py-2 rounded-md text-gray-600 hover:bg-gray-100 hover:text-blue-600 transition-all duration-200 font-medium">
                  首页
                </Link>
                <Link 
                  to="/products" 
                  className="px-4 py-2 rounded-md text-gray-600 hover:bg-gray-100 hover:text-blue-600 transition-all duration-200 font-medium">
                  产品
                </Link>
              </div>
            </div>
          </nav>
          <div className="max-w-4xl mx-auto px-4 pb-8">
            <Outlet />
          </div>
        </div>
      );
    }

    function Home() {
      return (
        <div className="page-enter bg-white p-8 rounded-lg shadow-sm border border-gray-100">
          <h1 className="text-3xl font-bold text-gray-900 mb-4">🏠 首页</h1>
          <p className="text-gray-600 leading-relaxed">
            这是嵌套路由的首页。布局组件通过 <code className="bg-gray-100 px-2 py-1 rounded">Outlet</code> 渲染子路由。
          </p>
        </div>
      );
    }

    function Products() {
      return (
        <div className="page-enter bg-white p-8 rounded-lg shadow-sm border border-gray-100">
          <h2 className="text-2xl font-bold text-gray-900 mb-4">📦 产品列表</h2>
          <div className="space-y-2">
            <Link 
              to="/products/1" 
              className="block px-4 py-2 bg-blue-50 text-blue-700 rounded-md hover:bg-blue-100 transition-colors">
              产品 1
            </Link>
            <Link 
              to="/products/2" 
              className="block px-4 py-2 bg-blue-50 text-blue-700 rounded-md hover:bg-blue-100 transition-colors">
              产品 2
            </Link>
            <Link 
              to="/products/3" 
              className="block px-4 py-2 bg-blue-50 text-blue-700 rounded-md hover:bg-blue-100 transition-colors">
              产品 3
            </Link>
          </div>
        </div>
      );
    }

    function ProductDetail() {
      const { id } = useParams();
      return (
        <div className="page-enter bg-white p-8 rounded-lg shadow-sm border border-gray-100">
          <h2 className="text-2xl font-bold text-gray-900 mb-4">📋 产品详情</h2>
          <p className="text-gray-600 mb-4">
            产品 ID: <span className="font-mono font-bold text-blue-600 text-xl">{id}</span>
          </p>
          <div className="mt-6 p-4 bg-green-50 text-green-700 rounded-md border border-green-100">
            这是嵌套在产品路由下的详情页面
          </div>
        </div>
      );
    }

    function App() {
      return (
        <HashRouter>
          <Routes>
            <Route path="/" element={<Layout />}>
              <Route index element={<Home />} />
              <Route path="products" element={<Products />} />
              <Route path="products/:id" element={<ProductDetail />} />
            </Route>
          </Routes>
        </HashRouter>
      );
    }

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