详解Vue单页应用前端路由实现原理:History与Hash模式
本文深入解析Vue单页应用(SPA)的两种主流前端路由实现方式:History模式与Hash模式。详细阐述History API中pushState、replaceState及popstate事件的工作机制,以及Hash模式下onhashchange事件的触发逻辑。对比两种模式在URL结构、状态存储及浏览器行为上的差异,并针对History模式可能引发的404错误,提供服务端Fallback配置方案,帮助开发者理解并正确实现前端路由。
前端路由实现概述
在单页应用(SPA)的开发中,前端路由通常采用两种主要实现方式:
- window.history
- location.hash
接下来将分别介绍这两种方式的具体实现细节。
History模式详解
一、History模式
1. History基本介绍
window.history 对象用于存储浏览器的历史记录,编写时通常省略 window 前缀。作为实现 SPA 前端路由的主流方案,它提供了几个核心方法:
- history.back():功能等同于点击浏览器的后退按钮。
- history.forward():功能等同于点击浏览器的前进按钮。
- history.go(n):接收一个整数参数以跳转到指定历史步数,例如 go(1) 等同于 forward(),go(-1) 等同于 back(),go(0) 则相当于刷新当前页面。
- 若尝试移动到的位置超出访问历史的边界,上述三个方法不会抛出错误,而是静默失败。
HTML5 标准在 history 对象中引入了 pushState() 和 replaceState() 方法,允许向历史栈中手动添加数据,模拟 URL 变化的效果(此前仅 URL 变化才会更新历史栈)。这一机制使得模拟浏览器的前进、后退功能成为可能,也是当前前端路由实现的基础。
2. history.pushState
pushState(stateObj, title, url) 方法用于向历史栈写入数据。其第一个参数是状态对象(大小限制为不超过 640kB),第二个参数是页面标题,第三个参数是相对路径的 URL。
- stateObj:与指定网址关联的状态对象,当 popstate 事件触发时,该对象会作为参数传入回调函数。若无需使用此对象,可填入 null。
- title:新页面的标题,目前所有浏览器均忽略此参数,因此此处通常填入 null。
- 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 事件,以下情况会触发该事件:
- 直接修改浏览器地址,在末尾增加或更改 #hash 部分;
- 通过修改 location.href 或 location.hash 的值;
- 通过点击带有锚点的链接;
- 浏览器的前进或后退操作可能导致 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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
Vue.js官网地址是多少?区分国际站与中文文档入口
2026-11-05 16:29:53
Vue/React 项目中怎么封装复用 flv.js 播放器组件
2026-11-03 19:42:36
Vue.js 动态路由权限控制与菜单生成实战方案
2026-11-03 13:46:47
Vue.js 企业级项目架构搭建与目录规范实战
2026-11-03 12:25:32
Vue 或 React 项目中集成 Swiper.js 的最佳实践
2026-11-02 10:29:42
