Skip to content

Vue路由模式详解:Hash vs History

作为一名前端面试官,我会期望候选人能够清晰、深入地讲解 Vue 路由中 hash 模式和 history 模式的实现原理,而不仅仅是停留在表面如何配置使用。一个好的回答应该包含对底层浏览器 API 的理解、两种模式的优缺点对比,以及 history 模式所涉及的服务端配置问题。

以下是我为你梳理的回答思路和要点,你可以根据这个框架来组织你的回答:

回答思路

你可以按照以下"总-分-总"的结构来回答:

  1. 开场白: 首先,简明扼要地说明 vue-router 提供了两种主要的路由模式:hash 模式和 history 模式,它们主要区别在于 URL 的表现形式和对浏览器历史记录的利用方式不同。
  2. 分述两种模式的实现原理: 这是回答的核心部分。你需要详细解释每种模式是如何工作的。
    • Hash 模式: 讲解其如何利用 URL 的 hash(#)部分,以及相关的浏览器事件。
    • History 模式: 讲解其如何利用 HTML5 History API,并强调其与服务端的关联。
  3. 对比总结和场景分析: 对比两种模式的优缺点,并说明在什么场景下应该选择哪种模式。
  4. 结尾: 可以补充说明在 Vue 3 中如何通过 createWebHashHistorycreateWebHistory 来创建不同的路由模式实例,以展示你对新版 Vue 的了解。

回答范例

面试官您好,关于 Vue 路由的 hash 模式和 history 模式,我的理解如下:

这两种模式都是为了在不重新加载整个页面的情况下,实现前端路由,更新视图。它们的核心区别在于如何处理和监听 URL 的变化。

一、Hash 模式 (Hash Mode)

1. URL 特点:hash 模式的 URL 中会带有一个 # 号,比如 http://localhost:8080/#/home# 号后面的部分(/home)就是我们前端路由的路径。

2. 实现原理:hash 模式的实现主要依赖于浏览器 URL 的哈希(hash)部分和 window 对象上的 hashchange 事件。

  • URL 变化感知: 当 URL 的 hash 部分发生改变时,浏览器会触发 hashchange 事件。vue-router 内部会监听这个事件。
  • 路由更新: 一旦监听到 hashchange 事件,vue-router 就会根据新的 hash 值去匹配对应的路由规则,然后找到相应的组件并重新渲染视图。
  • URL 改变方式: 我们可以通过 a 标签的 href 属性(例如 <a href="#/about">),或者在 JavaScript 中直接修改 window.location.hash 的值来改变 URL 的 hash,这两种方式都会触发 hashchange 事件,但不会向服务器发送新的请求,也不会刷新页面。这是因为 URL 中 # 及其后面的内容,在 HTTP 请求中是不会被发送到服务器端的。

3. 优点:

  • 兼容性好: hashchange 事件在所有现代浏览器甚至一些旧版浏览器(如 IE8)中都得到了支持。
  • 无需服务端配置: 由于 hash 部分不会被包含在 HTTP 请求中,所以无论 URL 如何变化,服务端收到的请求都是针对根目录的 index.html。因此,不需要对服务端做任何特殊配置。

4. 缺点:

  • URL 不美观: URL 中多了一个 # 号,对于追求美观的 URL 的项目来说,可能不是最佳选择。
  • 对 SEO 不友好(早期问题): 虽然现在大部分搜索引擎已经能够抓取带 hash 的 URL,但在过去,这被认为对搜索引擎优化(SEO)不友好。

二、History 模式 (History Mode)

1. URL 特点:history 模式的 URL 看起来就像是传统的 URL,非常美观,例如 http://localhost:8080/home

2. 实现原理:history 模式的实现依赖于 HTML5 新增的 History API,主要是 history.pushState()history.replaceState() 方法,以及 window 对象上的 popstate 事件。

  • URL 改变方式: 当我们通过 <router-link> 组件进行导航时,vue-router 内部会调用 history.pushState()history.replaceState()。这两个方法可以在不刷新页面的情况下,改变浏览器的 URL,并将新的状态添加到浏览器的历史记录栈中。
  • URL 变化感知:
    • pushStatereplaceState 不会主动触发任何事件。
    • 但是,当用户点击浏览器的前进或后退按钮时,会触发 popstate 事件。vue-router 会监听这个事件,从而得知 URL 发生了变化,然后去匹配路由并更新视图。
  • 服务端配置要求: 这是一个非常关键的点。因为 history 模式的 URL 会被完整地发送到服务器,当用户在浏览器中直接访问 http://localhost:8080/home 或者刷新这个页面时,浏览器会向服务器请求 /home 这个路径。如果服务器没有对应的处理,就会返回 404 错误。
    • 解决方案: 我们需要在服务端配置一个"后备路由",即当服务器接收到的请求路径没有匹配到任何静态资源时,就总是返回我们的主 index.html 文件。这样,前端的 Vue 应用就可以接管路由,并显示正确的页面。例如,在 Nginx 中,我们可以这样配置:
      nginx
      location / {
        try_files $uri $uri/ /index.html;
      }

3. 优点:

  • URL 美观: URL 更加简洁、优雅,符合用户的认知习惯。
  • 对 SEO 友好: 搜索引擎可以更好地抓取和索引这种结构的 URL。

4. 缺点:

  • 需要服务端支持: 部署时需要服务端进行额外的配置,否则刷新页面或直接访问会 404。
  • 兼容性问题: History API 是 HTML5 的新特性,在一些老旧的浏览器上(如 IE9 及以下)不被支持。

三、总结对比

特性Hash 模式History 模式
URLhttp://a.com/#/home (带#)http://a.com/home (不带#)
核心 APIonhashchangehistory.pushState, onpopstate
服务端配置无需配置需要配置后备路由到index.html
兼容性兼容性好兼容现代浏览器 (IE10+)
美观度较差

在选择上,如果项目对 URL 的美观度要求不高,或者后端配置比较麻烦,可以选择 hash 模式,因为它简单、兼容性好。如果项目对 URL 有要求,并且不担心后端配置的额外工作,那么 history 模式是更好的选择。目前大部分新项目都会优先考虑使用 history 模式。

四、Vue 3 中的实现

最后,在 Vue 3 中,我们通过 createRouter 创建路由实例时,可以通过 history 选项来指定模式,例如:

  • createWebHashHistory() 用于 hash 模式
  • createWebHistory() 用于 history 模式

这使得配置更加模块化和清晰。

javascript
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'

// History 模式
const router = createRouter({
  history: createWebHistory(),
  routes: [...]
})

// Hash 模式
const router = createRouter({
  history: createWebHashHistory(),
  routes: [...]
})

以上就是我对 Vue 路由两种模式实现原理的理解。

基于 VitePress 构建