基础路由设置

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 (HashRouter)</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, useLocation, NavLink } = window.ReactRouterDOM;
      const { useState } = React;

      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">
              欢迎来到 React Router 示例应用。
              <br />
              我们使用 <code>HashRouter</code> 来确保在静态环境和 iframe 中路由能正常工作。
            </p>
            <div className="mt-6 p-4 bg-blue-50 text-blue-700 rounded-md border border-blue-100">
              当前路径: <span className="font-mono font-bold">/</span>
            </div>
          </div>
        );
      }

      function About() {
        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">
              这是一个关于页面的演示。
              <br />
              你可以随意刷新页面,由于使用了 Hash 模式,你不会遇到 404 错误。
            </p>
            <div className="mt-6 p-4 bg-green-50 text-green-700 rounded-md border border-green-100">
              当前路径: <span className="font-mono font-bold">/about</span>
            </div>
          </div>
        );
      }

      function NotFound() {
          return (
              <div className="page-enter bg-red-50 p-8 rounded-lg border border-red-100 text-center">
                  <h1 className="text-2xl font-bold text-red-600 mb-2">404</h1>
                  <p className="text-red-500">页面未找到</p>
                  <Link to="/" className="mt-4 inline-block px-4 py-2 bg-red-100 text-red-700 rounded hover:bg-red-200 transition-colors">
                      返回首页
                  </Link>
              </div>
          )
      }

      function Navigation() {
        const [isMenuOpen, setIsMenuOpen] = useState(false);
        
        const getLinkClass = ({ isActive }) => {
          const baseClass = "px-4 py-2 rounded-md transition-all duration-200 font-medium";
          const activeClass = "bg-blue-600 text-white shadow-md transform scale-105";
          const inactiveClass = "text-gray-600 hover:bg-gray-100 hover:text-blue-600";
          return isActive ? `${baseClass} ${activeClass}` : `${baseClass} ${inactiveClass}`;
        };

        const getMobileLinkClass = ({ isActive }) => {
          const baseClass = "block px-4 py-2 rounded-md transition-all duration-200 font-medium";
          const activeClass = "bg-blue-600 text-white shadow-md";
          const inactiveClass = "text-gray-600 hover:bg-gray-100 hover:text-blue-600";
          return isActive ? `${baseClass} ${activeClass}` : `${baseClass} ${inactiveClass}`;
        };

        const toggleMenu = () => {
          setIsMenuOpen(!isMenuOpen);
        };

        return (
          <nav className="bg-white shadow-sm border-b border-gray-200 mb-8 sticky top-0 z-10">
            <div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
              <div className="flex justify-between h-16 items-center">
                <div className="flex items-center flex-1">
                  <span className="text-lg sm:text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-indigo-600 mr-4 sm:mr-8">
                    RouterDemo
                  </span>
                  {/* 桌面端导航链接 */}
                  <div className="hidden md:flex space-x-4">
                    <NavLink to="/" className={getLinkClass} end>
                      首页
                    </NavLink>
                    <NavLink to="/about" className={getLinkClass}>
                      关于
                    </NavLink>
                    <NavLink to="/non-existent" className={getLinkClass}>
                      不存在的页面
                    </NavLink>
                  </div>
                </div>
                {/* 移动端汉堡菜单按钮 */}
                <button
                  onClick={toggleMenu}
                  className="md:hidden p-2 rounded-md text-gray-600 hover:bg-gray-100 hover:text-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
                  aria-label="切换菜单"
                >
                  {isMenuOpen ? (
                    <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                    </svg>
                  ) : (
                    <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
                    </svg>
                  )}
                </button>
              </div>
              {/* 移动端折叠菜单 */}
              <div className={`md:hidden transition-all duration-300 ease-in-out overflow-hidden ${isMenuOpen ? 'max-h-64 opacity-100' : 'max-h-0 opacity-0'}`}>
                <div className="py-4 space-y-2 border-t border-gray-200">
                  <NavLink 
                    to="/" 
                    className={getMobileLinkClass}
                    end
                    onClick={() => setIsMenuOpen(false)}
                  >
                    首页
                  </NavLink>
                  <NavLink 
                    to="/about" 
                    className={getMobileLinkClass}
                    onClick={() => setIsMenuOpen(false)}
                  >
                    关于
                  </NavLink>
                  <NavLink 
                    to="/non-existent" 
                    className={getMobileLinkClass}
                    onClick={() => setIsMenuOpen(false)}
                  >
                    不存在的页面
                  </NavLink>
                </div>
              </div>
            </div>
          </nav>
        );
      }

      function App() {
        return (
          <HashRouter>
            <div className="min-h-screen flex flex-col">
              <Navigation />
              
              <main className="flex-grow max-w-3xl mx-auto w-full px-4 sm:px-6 lg:px-8 pb-12">
                <Routes>
                  <Route path="/" element={<Home />} />
                  <Route path="/about" element={<About />} />
                  <Route path="*" element={<NotFound />} />
                </Routes>
              </main>

              <footer className="bg-gray-100 border-t border-gray-200 py-6 text-center text-sm text-gray-500">
                <p>React Router DOM v6 演示 (HashRouter 模式)</p>
              </footer>
            </div>
          </HashRouter>
        );
      }

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