前言
createHashRouter
是 React Router
提供的一种特殊路由器,它使用 URL 的哈希部分(#)
来处理客户端路由。
这种路由方式特别适用于静态网站托管服务(如 GitHub Pages)或无法配置服务器以支持 HTML5 History API
的场景。
一、createHashRouter 的主要用途
- 无服务器配置要求:不需要服务器端重写规则
- 静态网站兼容:完美适配 GitHub Pages 等静态托管服务
- 旧浏览器支持:兼容不支持 HTML5 History API 的浏览器
- 简单部署:避免处理服务器配置问题
- 本地开发便利:在本地文件系统上也能正常工作
二、createHashRouter 与 createBrowserRouter 的关键区别
三、createHashRouter
完整代码示例
3.1、 基础路由配置
// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import {
createHashRouter,
RouterProvider,
Link,
Outlet,
useLocation
} from 'react-router-dom';
// 页面组件
function Home() {
return (
<div className="page home">
<h1>首页</h1>
<p>欢迎使用哈希路由示例应用</p>
<div className="page-nav">
<Link to="/about" className="nav-link">关于我们</Link>
<Link to="/products" className="nav-link">产品列表</Link>
</div>
</div>
);
}
function About() {
return (
<div className="page about">
<h1>关于我们</h1>
<p>我们是一家专注于前端技术的公司</p>
<Link to="/" className="back-link">返回首页</Link>
</div>
);
}
// 布局组件
function RootLayout() {
const location = useLocation();
return (
<div className="app">
<header className="app-header">
<h1 className="logo">哈希路由示例</h1>
<div className="url-display">
当前路由: <code>{location.pathname || '/'}</code>
</div>
<nav className="main-nav">
<Link to="/" className="nav-item">首页</Link>
<Link to="/about" className="nav-item">关于</Link>
<Link to="/products" className="nav-item">产品</Link>
</nav>
</header>
<main className="app-content">
<Outlet /> {/* 子路由渲染位置 */}
</main>
<footer className="app-footer">
<p>当前使用: <code>createHashRouter</code> | © 2023 React Router 示例</p>
</footer>
</div>
);
}
// 创建哈希路由配置
const router = createHashRouter([
{
path: "/",
element: <RootLayout />,
children: [
{
index: true,
element: <Home />
},
{
path: "about",
element: <About />
},
{
path: "products",
element: <ProductsList />,
children: [
{
path: ":productId",
element: <ProductDetail />
}
]
}
]
}
]);
// 产品列表组件
function ProductsList() {
const products = [
{ id: 1, name: 'React 教程', price: 99 },
{ id: 2, name: 'Node.js 实战', price: 129 },
{ id: 3, name: 'TypeScript 指南', price: 89 }
];
return (
<div className="page products">
<h1>产品列表</h1>
<div className="products-grid">
{products.map(product => (
<div key={product.id} className="product-card">
<h3>
<Link to={`/products/${product.id}`}>
{product.name}
</Link>
</h3>
<p>价格: ¥{product.price}</p>
</div>
))}
</div>
<Link to="/" className="back-link">返回首页</Link>
</div>
);
}
// 产品详情组件
function ProductDetail() {
const { productId } = useParams();
const products = {
1: { id: 1, name: 'React 教程', description: '深入学习 React 框架', price: 99 },
2: { id: 2, name: 'Node.js 实战', description: '构建高性能后端应用', price: 129 },
3: { id: 3, name: 'TypeScript 指南', description: '掌握类型安全的JavaScript', price: 89 }
};
const product = products[productId];
if (!product) {
return (
<div className="error">
<h2>产品未找到</h2>
<p>没有找到ID为 {productId} 的产品</p>
<Link to="/products" className="back-link">返回产品列表</Link>
</div>
);
}
return (
<div className="page product-detail">
<h1>{product.name}</h1>
<p className="description">{product.description}</p>
<p className="price">价格: ¥{product.price}</p>
<Link to="/products" className="back-link">返回产品列表</Link>
</div>
);
}
// 渲染应用
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
3.2、 带数据加载的高级示例
// 在 main.jsx 中添加以下内容
import { useLoaderData } from 'react-router-dom';
// 创建模拟API函数
async function fetchProduct(productId) {
// 模拟API延迟
await new Promise(resolve => setTimeout(resolve, 500));
const products = {
1: { id: 1, name: 'React 教程', description: '深入学习 React 框架',
price: 99, reviews: 24 },
2: { id: 2, name: 'Node.js 实战', description: '构建高性能后端应用',
price: 129, reviews: 18 },
3: { id: 3, name: 'TypeScript 指南', description: '掌握类型安全的JavaScript',
price: 89, reviews: 32 }
};
return products[productId] || null;
}
// 更新产品详情组件使用加载器
function ProductDetail() {
const product = useLoaderData();
if (!product) {
return (
<div className="error">
<h2>产品未找到</h2>
<p>请求的产品不存在</p>
<Link to="/products" className="back-link">返回产品列表</Link>
</div>
);
}
return (
<div className="page product-detail">
<h1>{product.name}</h1>
<p className="description">{product.description}</p>
<div className="product-meta">
<span className="price">价格: ¥{product.price}</span>
<span className="reviews">评价: {product.reviews} 条</span>
</div>
<Link to="/products" className="back-link">返回产品列表</Link>
</div>
);
}
// 添加产品加载器
async function productLoader({ params }) {
const product = await fetchProduct(params.productId);
if (!product) {
throw new Response('Product Not Found', { status: 404 });
}
return product;
}
// 更新路由配置
const router = createHashRouter([
{
path: "/",
element: <RootLayout />,
children: [
// ...其他路由
{
path: "products",
element: <ProductsList />,
children: [
{
path: ":productId",
element: <ProductDetail />,
loader: productLoader,
errorElement: <div className="error">产品加载失败</div>
}
]
}
]
}
]);
3.3、 表单处理示例
// 添加联系表单页面
function Contact() {
return (
<div className="page contact">
<h1>联系我们</h1>
<ContactForm />
<Link to="/" className="back-link">返回首页</Link>
</div>
);
}
// 联系表单组件
function ContactForm() {
return (
<form className="contact-form" method="post" action="/contact">
<div className="form-group">
<label htmlFor="name">姓名</label>
<input
type="text"
id="name"
name="name"
required
placeholder="请输入您的姓名"
/>
</div>
<div className="form-group">
<label htmlFor="email">邮箱</label>
<input
type="email"
id="email"
name="email"
required
placeholder="请输入您的邮箱"
/>
</div>
<div className="form-group">
<label htmlFor="message">留言</label>
<textarea
id="message"
name="message"
required
rows="5"
placeholder="请输入您的留言内容"
></textarea>
</div>
<button type="submit" className="submit-btn">提交</button>
</form>
);
}
// 联系表单处理器
async function contactAction({ request }) {
const formData = await request.formData();
const data = Object.fromEntries(formData);
// 在实际应用中,这里会发送数据到服务器
console.log('收到联系表单:', data);
// 显示成功消息
return { success: true, message: '感谢您的留言,我们会尽快回复!' };
}
// 更新路由配置
const router = createHashRouter([
{
path: "/",
element: <RootLayout />,
children: [
// ...其他路由
{
path: "contact",
element: <Contact />,
action: contactAction
}
]
}
]);
四、createHashRouter
核心特性详解
4.1、哈希路由工作原理
哈希路由使用 URL 的哈希部分(# 之后的内容)来管理应用状态:
text
http://example.com/#/about
http://example.com/#/products/123
- 哈希部分的变化不会触发页面刷新
- 服务器只接收
http://example.com/
请求 - 路由完全在客户端处理
4.2、 路由配置
配置方式与 createBrowserRouter
相同:
const router = createHashRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: "about", element: <About /> },
{ path: "contact", element: <Contact /> }
]
}
]);
4.3、 数据加载(loader
)
{
path: "products/:id",
element: <ProductDetail />,
loader: async ({ params }) => {
const response = await fetch(`/api/products/${params.id}`);
return response.json();
}
}
4.4、 表单处理(action
)
{
path: "contact",
element: <Contact />,
action: async ({ request }) => {
const formData = await request.formData();
// 处理表单数据
return redirect('/thank-you');
}
}
4.5、 错误处理
{
path: "/",
element: <Layout />,
errorElement: <GlobalError />,
children: [
{
path: "products/:id",
element: <ProductDetail />,
errorElement: <ProductError />,
loader: productLoader
}
]
}
4.6、 编程式导航
import { useNavigate } from 'react-router-dom';
function LoginButton() {
const navigate = useNavigate();
const handleLogin = () => {
// 执行登录逻辑...
navigate('/dashboard');
};
return <button onClick={handleLogin}>登录</button>;
}
五、最佳实践
5.1、 部署到 GitHub Pages
- 创建 React 应用:
npx create-react-app my-app
- 安装 React Router:
npm install react-router-dom
- 配置哈希路由
- 在 package.json 中添加:
json
"homepage": "https://<username>.github.io/<repo-name>",
"scripts": {
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
}
- 安装
gh-pages:npm install gh-pages --save-dev
- 部署:
npm run deploy
5.2、 处理深链接
由于哈希路由不需要服务器配置,深链接可以直接工作:
text
https://username.github.io/repo/#/products/123
5.3、 SEO 优化策略
虽然哈希路由对 SEO 不友好,但可以采取以下策略:
- 使用服务端渲染(SSR)替代
- 添加预渲染(如使用 react-snap)
- 提供静态元数据:
html
<meta name="description" content="产品详情页">
5.4、 性能优化
代码分割:
const ProductDetail = React.lazy(() => import('./ProductDetail'));
{
path: "products/:id",
element: (
<React.Suspense fallback={<div>加载中...</div>}>
<ProductDetail />
</React.Suspense>
)
}
数据预加载:
<Link
to="/products/123"
onMouseEnter={() => preloadProductData(123)}
>
产品详情
</Link>
六、createHashRouter
使用场景推荐
- GitHub Pages 项目:个人作品集、项目文档
- 静态网站:营销页面、活动页面
- 本地文件协议:file:// 协议下的应用
- 浏览器扩展:Chrome/Firefox 扩展的弹出页面
- 临时演示:快速分享原型演示
- 旧浏览器支持:需要兼容 IE9 等旧浏览器
总结
createHashRouter
是 React Router
提供的一种特殊路由解决方案,它通过 URL 的哈希部分实现客户端路由
,
主要特点包括:
- 无需服务器配置:适用于静态托管环境
- 广泛兼容性:支持所有现代和旧版浏览器
- 简单易用:与标准路由 API 完全兼容
- 快速部署:特别适合 GitHub Pages 等平台
虽然哈希路由在 URL 美观性和 SEO 方面存在不足,但在以下场景中它是理想选择:
静态网站托管(GitHub Pages、Netlify、Vercel 等)
无法配置服务器重定向规则的环境
需要支持旧版浏览器的应用
本地文件系统运行的应用程序
通过结合 React Router
的数据加载、表单处理和错误边界功能,createHashRouter
可以构建功能完善的单页应用,同时享受简单部署的便利性。