React第六十节 Router中createHashRouter的具体使用详解及案例分析

发布于:2025-06-13 ⋅ 阅读:(16) ⋅ 点赞:(0)

前言

createHashRouterReact Router 提供的一种特殊路由器,它使用 URL 的哈希部分(#)来处理客户端路由。
这种路由方式特别适用于静态网站托管服务(如 GitHub Pages)或无法配置服务器以支持 HTML5 History API 的场景。

一、createHashRouter 的主要用途

  1. 无服务器配置要求:不需要服务器端重写规则
  2. 静态网站兼容:完美适配 GitHub Pages 等静态托管服务
  3. 旧浏览器支持:兼容不支持 HTML5 History API 的浏览器
  4. 简单部署:避免处理服务器配置问题
  5. 本地开发便利:在本地文件系统上也能正常工作

二、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
  1. 哈希部分的变化不会触发页面刷新
  2. 服务器只接收 http://example.com/ 请求
  3. 路由完全在客户端处理

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

  1. 创建 React 应用:npx create-react-app my-app
  2. 安装 React Router:npm install react-router-dom
  3. 配置哈希路由
  4. 在 package.json 中添加:
json
"homepage": "https://<username>.github.io/<repo-name>",
"scripts": {
  "predeploy": "npm run build",
  "deploy": "gh-pages -d build"
}
  1. 安装 gh-pages:npm install gh-pages --save-dev
  2. 部署:npm run deploy

5.2、 处理深链接

由于哈希路由不需要服务器配置,深链接可以直接工作:

text
https://username.github.io/repo/#/products/123

5.3、 SEO 优化策略

虽然哈希路由对 SEO 不友好,但可以采取以下策略:

  1. 使用服务端渲染(SSR)替代
  2. 添加预渲染(如使用 react-snap)
  3. 提供静态元数据:
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使用场景推荐

  1. GitHub Pages 项目:个人作品集、项目文档
  2. 静态网站:营销页面、活动页面
  3. 本地文件协议:file:// 协议下的应用
  4. 浏览器扩展:Chrome/Firefox 扩展的弹出页面
  5. 临时演示:快速分享原型演示
  6. 旧浏览器支持:需要兼容 IE9 等旧浏览器

总结

createHashRouterReact Router 提供的一种特殊路由解决方案,它通过 URL 的哈希部分实现客户端路由

主要特点包括:

  1. 无需服务器配置:适用于静态托管环境
  2. 广泛兼容性:支持所有现代和旧版浏览器
  3. 简单易用:与标准路由 API 完全兼容
  4. 快速部署:特别适合 GitHub Pages 等平台

虽然哈希路由在 URL 美观性和 SEO 方面存在不足,但在以下场景中它是理想选择:

  1. 静态网站托管(GitHub Pages、Netlify、Vercel 等)

  2. 无法配置服务器重定向规则的环境

  3. 需要支持旧版浏览器的应用

  4. 本地文件系统运行的应用程序

通过结合 React Router 的数据加载、表单处理和错误边界功能,createHashRouter 可以构建功能完善的单页应用,同时享受简单部署的便利性。