基础路由设置
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>