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

前言

createHashRouterReact Router 提供的一种特殊路由器 ,它使用 URL 的哈希部分(#)来处理客户端路由。

这种路由方式特别适用于静态网站托管服务 (如 GitHub Pages)或无法配置服务器 以支持 HTML5 History API 的场景。

一、createHashRouter 的主要用途

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

二、createHashRouter 与 createBrowserRouter 的关键区别

三、createHashRouter 完整代码示例

3.1、 基础路由配置

javascript 复制代码
// 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、 带数据加载的高级示例

javascript 复制代码
// 在 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、 表单处理示例

javascript 复制代码
// 添加联系表单页面
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 的哈希部分(# 之后的内容)来管理应用状态:

javascript 复制代码
text
http://example.com/#/about
http://example.com/#/products/123
  1. 哈希部分的变化不会触发页面刷新
  2. 服务器只接收 http://example.com/ 请求
  3. 路由完全在客户端处理

4.2、 路由配置

配置方式与 createBrowserRouter 相同:

javascript 复制代码
const router = createHashRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      { index: true, element: <Home /> },
      { path: "about", element: <About /> },
      { path: "contact", element: <Contact /> }
    ]
  }
]);

4.3、 数据加载(loader

javascript 复制代码
{
  path: "products/:id",
  element: <ProductDetail />,
  loader: async ({ params }) => {
    const response = await fetch(`/api/products/${params.id}`);
    return response.json();
  }
}

4.4、 表单处理(action

javascript 复制代码
{
  path: "contact",
  element: <Contact />,
  action: async ({ request }) => {
    const formData = await request.formData();
    // 处理表单数据
    return redirect('/thank-you');
  }
}

4.5、 错误处理

javascript 复制代码
{
  path: "/",
  element: <Layout />,
  errorElement: <GlobalError />,
  children: [
    {
      path: "products/:id",
      element: <ProductDetail />,
      errorElement: <ProductError />,
      loader: productLoader
    }
  ]
}

4.6、 编程式导航

javascript 复制代码
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 中添加:
javascript 复制代码
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、 处理深链接

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

javascript 复制代码
text
https://username.github.io/repo/#/products/123

5.3、 SEO 优化策略

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

  1. 使用服务端渲染(SSR)替代
  2. 添加预渲染(如使用 react-snap)
  3. 提供静态元数据:
javascript 复制代码
html
<meta name="description" content="产品详情页">

5.4、 性能优化

代码分割:

javascript 复制代码
const ProductDetail = React.lazy(() => import('./ProductDetail'));

{
  path: "products/:id",
  element: (
    <React.Suspense fallback={<div>加载中...</div>}>
      <ProductDetail />
    </React.Suspense>
  )
}

数据预加载:

javascript 复制代码
<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 可以构建功能完善的单页应 用,同时享受简单部署的便利性。

相关推荐
石小石Orz1 分钟前
被谷歌插件劝退,我半小时学会了油猴脚本开发
前端
安心不心安15 分钟前
React状态管理——zustand
javascript·react.js·ecmascript
阁下何不同风起?20 分钟前
前端导出PDF(适配ios Safari浏览器)
前端·pdf
zhangxingchao28 分钟前
Flutter网络编程与数据存储技术
前端
啪叽29 分钟前
探索鲜为人知的浏览器API:document.currentScript的实用案例
前端·javascript·dom
Jolyne_32 分钟前
前端发送多次请求,怎么保证请求参数与请求对应?
react.js·面试
DuxWeb36 分钟前
为什么 React 如此简单:5分钟理解核心概念,快速上手开发
前端·react.js
陈随易1 小时前
VSCode v1.101发布,MCP极大增强关联万物,基于VSCode的操作系统雏形已初见端倪
前端·后端·程序员
工呈士1 小时前
Vite 及生态环境:新时代的构建工具
前端·面试
然我1 小时前
从 Callback 地狱到 Promise:手撕 JavaScript 异步编程核心
前端·javascript·html