MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

React 使用 BrowserRouter 与 HashRouter

2023-05-113.3k 阅读

React 路由概述

在 React 应用开发中,路由(Routing)是一个至关重要的概念。它允许我们根据不同的 URL 来呈现不同的组件,从而构建单页应用(SPA)。单页应用能够在不重新加载整个页面的情况下更新页面内容,为用户提供流畅的交互体验。React 中有多种路由解决方案,其中 BrowserRouterHashRouter 是 React Router 库中两个常用的路由组件,它们分别基于浏览器的历史记录 API 和 URL 中的哈希(hash)来实现路由功能。

BrowserRouter

1. 原理

BrowserRouter 基于 HTML5 的 history API(pushStatereplaceStatepopstate 事件)来实现路由功能。当 URL 发生变化时,pushStatereplaceState 方法会被调用,浏览器的历史记录会被更新,但页面不会重新加载。同时,popstate 事件会监听浏览器的前进和后退按钮的点击,以便相应地更新应用的 UI。

2. 使用方法

首先,确保已经安装了 react-router-dom 库。在 React 项目中,可以通过以下命令安装:

npm install react-router-dom

或者使用 yarn:

yarn add react-router-dom

然后在代码中引入并使用 BrowserRouter

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Contact from './components/Contact';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </Router>
  );
}

export default App;

在上述代码中:

  • react-router-dom 中引入了 BrowserRouter 并将其重命名为 Router(这是一种常见的做法)。
  • 使用 Routes 组件来定义多个路由规则。
  • 每个 Route 组件指定了一个 path 属性(表示 URL 路径)和一个 element 属性(表示当路径匹配时要渲染的组件)。

3. 优点

  • 美观的 URL:使用 BrowserRouter 生成的 URL 看起来更加简洁和自然,没有哈希符号(#),例如 https://example.com/about,这对于搜索引擎优化(SEO)更为友好,因为搜索引擎通常可以更好地解析和索引这种形式的 URL。
  • 符合现代 Web 标准:基于 HTML5 的 history API,是现代 Web 开发的标准方式,与传统的多页应用的 URL 模式相似,开发者和用户都更容易理解和接受。

4. 缺点

  • 服务器配置要求:由于 URL 直接映射到页面内容,在服务器端需要进行额外的配置。例如,在使用 Node.js 和 Express 搭建的服务器中,需要配置所有的请求都返回 React 应用的入口文件(通常是 index.html),以便 React Router 在客户端能够正确处理路由。否则,当用户直接访问某个深层路由(如 https://example.com/about)时,服务器可能会返回 404 错误。
  • 兼容性:虽然大多数现代浏览器都支持 HTML5 的 history API,但对于一些较旧的浏览器(如 Internet Explorer 10 及以下),需要使用 polyfill 来进行兼容处理。

HashRouter

1. 原理

HashRouter 使用 URL 中的哈希部分(即 # 后面的内容)来实现路由。当哈希值发生变化时,不会向服务器发送新的请求,浏览器也不会重新加载页面。JavaScript 可以通过监听 hashchange 事件来捕获哈希值的变化,并根据变化来更新应用的 UI。

2. 使用方法

同样先安装 react-router-dom 库,然后在代码中使用 HashRouter

import React from'react';
import { HashRouter as Router, Routes, Route } from'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Contact from './components/Contact';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </Router>
  );
}

export default App;

代码结构与 BrowserRouter 的使用非常相似,只是引入的是 HashRouter 并将其重命名为 Router

3. 优点

  • 简单的服务器配置:因为哈希部分不会被发送到服务器,所以服务器端不需要进行特殊配置。无论 URL 的哈希部分如何变化,服务器始终返回相同的 HTML 文件,所有的路由处理都在客户端由 React Router 完成。这对于一些静态网站托管服务或者不太容易进行服务器配置的环境(如 GitHub Pages)非常友好。
  • 兼容性好HashRouter 不依赖于 HTML5 的 history API,因此在所有浏览器中都能正常工作,包括那些不支持 history API 的旧版本浏览器。

4. 缺点

  • 不美观的 URL:URL 中带有哈希符号,例如 https://example.com/#/about,这种形式的 URL 看起来不够简洁和专业,可能会给用户带来一些困惑,尤其是对于不熟悉 Web 开发的用户。
  • SEO 问题:搜索引擎通常不会抓取 URL 中的哈希部分,这意味着包含哈希路由的页面可能在搜索引擎结果中的排名受到影响,因为搜索引擎无法准确理解哈希部分所代表的内容。

两者的选择

在选择 BrowserRouterHashRouter 时,需要考虑以下几个因素:

1. 项目的目标受众

如果项目主要面向现代浏览器用户,并且对 SEO 有较高要求,那么 BrowserRouter 是一个更好的选择。但如果项目需要兼容较旧的浏览器,或者对浏览器兼容性有严格要求,HashRouter 则更为合适。

2. 服务器环境

如果能够轻松地对服务器进行配置,以支持 BrowserRouter 所需的单页应用模式(例如在 Node.js + Express 服务器上进行配置),那么 BrowserRouter 可以提供更好的用户体验和 SEO 优势。然而,如果服务器环境不允许进行复杂的配置,或者项目是静态网站托管在如 GitHub Pages 上,HashRouter 则是更实际的选择。

3. SEO 需求

对于需要搜索引擎优化的应用,BrowserRouter 的无哈希 URL 结构更有利于搜索引擎爬虫的抓取和索引,能够提高应用在搜索结果中的可见性。而 HashRouter 由于哈希部分不被搜索引擎抓取,可能会对 SEO 产生一定的限制。

4. 应用类型

如果是内部工具或者对 URL 美观度和 SEO 要求不高的应用,HashRouter 的简单性和良好的兼容性可能更具吸引力。但对于面向公众的商业应用或者内容驱动的网站,BrowserRouter 通常是更好的选择,以提供更好的用户体验和搜索引擎友好性。

代码示例扩展

以下我们将通过一个稍微复杂一些的示例,展示 BrowserRouterHashRouter 在实际应用中的更多特性。

1. 使用 BrowserRouter 的示例

假设我们正在构建一个博客应用,具有首页、文章详情页、分类页面等。 首先,创建相关组件:

// Home.js
import React from'react';

const Home = () => {
  return (
    <div>
      <h1>欢迎来到博客首页</h1>
      <p>这里展示最新的文章列表...</p>
    </div>
  );
};

export default Home;

// Article.js
import React from'react';

const Article = ({ match }) => {
  const articleId = match.params.id;
  return (
    <div>
      <h1>文章详情</h1>
      <p>这是文章 {articleId} 的内容...</p>
    </div>
  );
};

export default Article;

// Category.js
import React from'react';

const Category = ({ match }) => {
  const category = match.params.category;
  return (
    <div>
      <h1>{category} 分类的文章</h1>
      <p>这里展示 {category} 分类下的文章...</p>
    </div>
  );
};

export default Category;

然后在 App.js 中使用 BrowserRouter 配置路由:

import React from'react';
import { BrowserRouter as Router, Routes, Route } from'react-router-dom';
import Home from './components/Home';
import Article from './components/Article';
import Category from './components/Category';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/article/:id" element={<Article />} />
        <Route path="/category/:category" element={<Category />} />
      </Routes>
    </Router>
  );
}

export default App;

在上述代码中:

  • Article 组件通过 match.params.id 获取文章的 ID,Category 组件通过 match.params.category 获取分类名称。
  • 这种动态路由参数的使用在实际应用中非常常见,可以根据不同的参数值展示不同的内容。

2. 使用 HashRouter 的示例

同样是这个博客应用,我们将 BrowserRouter 替换为 HashRouter。 在 App.js 中修改如下:

import React from'react';
import { HashRouter as Router, Routes, Route } from'react-router-dom';
import Home from './components/Home';
import Article from './components/Article';
import Category from './components/Category';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/article/:id" element={<Article />} />
        <Route path="/category/:category" element={<Category />} />
      </Routes>
    </Router>
  );
}

export default App;

可以看到,除了引入的是 HashRouter,其他代码基本保持不变。这也体现了 React Router 的灵活性,使得在两种路由方式之间切换相对容易。

嵌套路由

无论是 BrowserRouter 还是 HashRouter,都支持嵌套路由,这在构建复杂应用时非常有用。例如,在我们的博客应用中,文章详情页可能有相关推荐文章、评论等子路由。 首先,修改 Article.js 组件,添加子路由:

import React from'react';
import { Routes, Route } from'react-router-dom';
import RelatedArticles from './RelatedArticles';
import Comments from './Comments';

const Article = ({ match }) => {
  const articleId = match.params.id;
  return (
    <div>
      <h1>文章详情 {articleId}</h1>
      <Routes>
        <Route path="related" element={<RelatedArticles />} />
        <Route path="comments" element={<Comments />} />
      </Routes>
    </div>
  );
};

export default Article;

然后创建 RelatedArticles.jsComments.js 组件:

// RelatedArticles.js
import React from'react';

const RelatedArticles = () => {
  return (
    <div>
      <h2>相关推荐文章</h2>
      <p>这里展示相关的文章...</p>
    </div>
  );
};

export default RelatedArticles;

// Comments.js
import React from'react';

const Comments = () => {
  return (
    <div>
      <h2>评论</h2>
      <p>这里展示文章的评论...</p>
    </div>
  );
};

export default Comments;

此时,访问文章详情页的相关推荐文章或评论的 URL 会是类似 https://example.com/article/123/related(使用 BrowserRouter)或者 https://example.com/#/article/123/related(使用 HashRouter)。

导航

在 React 应用中,通常需要提供导航链接来方便用户在不同路由之间切换。react-router-dom 提供了 Link 组件来实现这一功能。 在 Home.js 组件中添加导航链接示例:

import React from'react';
import { Link } from'react-router-dom';

const Home = () => {
  return (
    <div>
      <h1>欢迎来到博客首页</h1>
      <p>这里展示最新的文章列表...</p>
      <ul>
        <li><Link to="/article/1">查看文章 1</Link></li>
        <li><Link to="/category/技术">技术分类文章</Link></li>
      </ul>
    </div>
  );
};

export default Home;

Link 组件的 to 属性指定了要导航到的路径。当用户点击链接时,应用会根据当前使用的路由方式(BrowserRouterHashRouter)来更新 URL 并渲染相应的组件。

总结与建议

BrowserRouterHashRouter 各有优缺点,在实际项目中需要根据具体需求进行选择。如果项目追求更好的用户体验、SEO 优化以及对现代浏览器的支持,BrowserRouter 是首选。而当项目需要兼容旧浏览器、简化服务器配置或者对 URL 美观度和 SEO 要求不高时,HashRouter 则是更合适的选择。同时,无论是哪种路由方式,都要充分利用 React Router 提供的各种功能,如动态路由参数、嵌套路由和导航组件等,来构建功能丰富、用户体验良好的单页应用。在开发过程中,还需要注意服务器端的配置(对于 BrowserRouter)以及兼容性处理(对于 HashRouter 可能存在的 URL 美观度和 SEO 问题),以确保应用在各种环境下都能稳定运行并满足业务需求。通过合理选择和使用路由方式,能够极大地提升 React 应用的质量和用户满意度。