详解Vue单页应用前端路由实现原理:History与Hash模式

作者:山岚

时间:2026-10-22

浏览:188

本文深入解析Vue单页应用(SPA)的两种主流前端路由实现方式:History模式与Hash模式。详细阐述History API中pushState、replaceState及popstate事件的工作机制,以及Hash模式下onhashchange事件的触发逻辑。对比两种模式在URL结构、状态存储及浏览器行为上的差异,并针对History模式可能引发的404错误,提供服务端Fallback配置方案,帮助开发者理解并正确实现前端路由。

文章目录

前端路由实现概述

在单页应用(SPA)的开发中,前端路由通常采用两种主要实现方式:

  1. window.history
  2. location.hash

接下来将分别介绍这两种方式的具体实现细节。

History模式详解

一、History模式

1. History基本介绍

window.history 对象用于存储浏览器的历史记录,编写时通常省略 window 前缀。作为实现 SPA 前端路由的主流方案,它提供了几个核心方法:

  1. history.back():功能等同于点击浏览器的后退按钮。
  2. history.forward():功能等同于点击浏览器的前进按钮。
  3. history.go(n):接收一个整数参数以跳转到指定历史步数,例如 go(1) 等同于 forward(),go(-1) 等同于 back(),go(0) 则相当于刷新当前页面。
  4. 若尝试移动到的位置超出访问历史的边界,上述三个方法不会抛出错误,而是静默失败。

HTML5 标准在 history 对象中引入了 pushState() 和 replaceState() 方法,允许向历史栈中手动添加数据,模拟 URL 变化的效果(此前仅 URL 变化才会更新历史栈)。这一机制使得模拟浏览器的前进、后退功能成为可能,也是当前前端路由实现的基础。

2. history.pushState

pushState(stateObj, title, url) 方法用于向历史栈写入数据。其第一个参数是状态对象(大小限制为不超过 640kB),第二个参数是页面标题,第三个参数是相对路径的 URL。

  1. stateObj:与指定网址关联的状态对象,当 popstate 事件触发时,该对象会作为参数传入回调函数。若无需使用此对象,可填入 null。
  2. title:新页面的标题,目前所有浏览器均忽略此参数,因此此处通常填入 null。
  3. url:新的网址,必须与当前页面遵循同源策略。浏览器的地址栏会显示该网址。

关于 pushState,需注意以下几点:

调用 pushState 方法不会导致页面刷新,仅改变 history 对象的状态,地址栏会随之更新。只有当用户触发前进、后退等导航事件(如调用 back() 或 forward())时,浏览器才会重新加载页面。

这里的 url 受同源策略限制,旨在防止恶意脚本伪造其他网站的 URL 以欺骗用户。若违反同源策略,将抛出错误。

3. history.replaceState

replaceState(stateObj, title, url) 与 pushState 的区别在于,它不是向历史栈添加新记录,而是替换当前历史记录。其余参数和行为与 pushState 完全一致。

4. popstate事件

定义:当同一文档的浏览历史(即 history 对象)发生变化时,会触发 popstate 事件。

注意:仅调用 pushState 或 replaceState 方法不会触发此事件。该事件仅在用户点击浏览器的前进/后退按钮,或通过 JavaScript 调用 back、forward、go 方法时触发。此外,该事件仅针对同一文档,若历史切换导致加载不同文档,则不会触发。

用法:可为 popstate 事件指定回调函数。该回调函数的参数是一个 event 对象,其 state 属性指向 pushState 和 replaceState 方法为当前 URL 提供的状态对象(即这两个方法的第一个参数)。

5. History模式实现SPA前端路由代码


<a class="api a">a.html</a>
<a class="api b">b.html</a>
 // 注册路由
 document.querySelectorAll('.api').forEach(item => {
  item.addEventListener('click', e => {
  e.preventDefault();
  let link = item.textContent;
  if (!!(window.history && history.pushState)) {
   // 支持History API
   window.history.pushState({name: 'api'}, link, link);
  } else {
   // 不支持,可使用一些Polyfill库来实现
  }
  }, false)
 });

 // 监听路由
 window.addEventListener('popstate', e => {
  console.log({
  location: location.href,
  state: e.state
  })
 }, false)

在 popstate 监听函数中打印的 e.state,即为 history.pushState() 传入的第一个参数,在此示例中为 {name: 'api'}。

Hash模式详解

二、Hash模式

1. Hash基本介绍

URL 中可以包含 hash 部分,格式如 http://localhost:9000/#/a.html。

window 对象中有一个 onhashchange 事件,以下情况会触发该事件:

  1. 直接修改浏览器地址,在末尾增加或更改 #hash 部分;
  2. 通过修改 location.href 或 location.hash 的值;
  3. 通过点击带有锚点的链接;
  4. 浏览器的前进或后退操作可能导致 hash 变化,前提是两个页面的 hash 值不同。

2. Hash模式实现SPA前端路由代码


 // 注册路由
 document.querySelectorAll('.api').forEach(item => {
  item.addEventListener('click', e => {
  e.preventDefault();
  let link = item.textContent;
  location.hash = link;
  }, false)
 });

 // 监听路由
 window.addEventListener('hashchange', e => {
  console.log({
  location: location.href,
  hash: location.hash
  })
 }, false)

Hash 模式与 History 模式均通过浏览器原生接口实现。此外,vue-router 为非浏览器环境提供了 abstract 模式,其原理是使用一个数组 stack 来模拟浏览器的历史记录栈。以上仅为核心逻辑,源码中还包含大量确保系统鲁棒性的辅助逻辑,同样值得学习。

两种模式对比

三、两种模式比较

  • pushState 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 Hash 模式仅能修改 # 后面的部分,因此只能设置与当前文档相同的 URL。
  • pushState 设置的新 URL 可以与当前 URL 完全相同,这也会将记录添加到栈中;而 Hash 模式设置的新值必须与原有值不同,才会触发将记录添加到栈中。
  • pushState 通过 stateObject 可以向历史记录中添加任意类型的数据;而 Hash 模式仅能添加短字符串。
  • pushState 可额外设置 title 属性以供后续使用。

History模式常见问题与解决

四、History模式的一个问题

对于单页应用,理想场景是在进入应用时仅加载 index.html,后续的网络操作通过 Ajax 完成,不再根据 URL 重新请求页面。但难免遇到特殊情况,例如用户直接在地址栏输入 URL 并回车,或浏览器重启重新加载应用等。

http://oursite.com/#/user/id // 如重新请求只会发送http://oursite.com/

因此,在 Hash 模式下遇到根据 URL 请求页面的情况通常不会出现问题。

而 History 模式会将 URL 修改得与正常请求后端的 URL 一致。

http://oursite.com/user/id

在此情况下重新向后端发送请求,如果后端没有配置对应 /user/id 的路由处理,则会返回 404 错误。

官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。同时这么做以后,服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。或者,如果是用 Node.js 作后台,可以使用服务端的路由来匹配 URL,当没有匹配到路由的时候返回 404,从而实现 fallback。

参考资料:

1、浏览器History API :https://developer.mozilla.org/zh-CN/docs/Web/API/History_API

2、解决History模式访问404的方案:https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%90%8E%E7%AB%AF%E9%85%8D%E7%BD%AE%E4%BE%8B%E5%AD%90

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章