前言
在现代前端开发中,我们习惯了页面不刷新但 URL 却在变化的体验。这背后除了 Hash 模式,最核心的功臣就是 History 对象。它不仅能控制页面的前进后退,还能在不触发请求的情况下修改地址栏。
一、 History 对象:用户的“航迹云”
window.history 存储了用户在当前窗口中访问的所有记录。为了隐私保护,你无法看到具体的 URL 列表,但可以通过它自由穿梭。
1. 基础属性
length:历史堆栈中的条目总数(包括当前页)。state:返回当前历史条目的状态对象副本。
2. 基础导航方法
-
back():后退一页。 -
forward():前进一页。 -
go(n):n > 0:前进n步。n < 0:后退n步。n = 0或不传:刷新当前页面。
二、 HTML5 状态管理:无刷新跳转的核心
HTML5 为 history 引入了两个重量级方法:pushState 和 replaceState。它们允许我们在不请求服务器的情况下,手动修改浏览器的地址栏。
1. pushState(state, title, url) —— 新增记录
-
作用:在历史记录栈中添加一个新条目。
-
参数:
state:一个 JSON 对象,用于存储自定义信息。title:目前大多数浏览器忽略,传""即可。url:新的 URL 地址,必须与当前页面同源。
2. replaceState(state, title, url) —— 替换记录
- 作用:修改当前的历史记录,而不是创建新的。
- 影响:调用后
history.length不会增加。
⚠️ 核心特点(避坑指南):
- 不刷新:调用这两个方法后,地址栏变了,但浏览器不会检查 URL 是否存在,也不会重新加载页面。
- 不触发 popstate:手动调用
pushState或replaceState不会触发popstate事件。
三、 popstate 事件:监听历史变动
虽然 pushState 不会触发 popstate,但浏览器的原生行为会触发它:
- 点击“后退”或“前进”按钮。
- 在 JS 中调用
back()、forward()或go()。
window.addEventListener('popstate', (event) => {
console.log("检测到路径变化,当前关联的状态数据:", event.state);
// 这里通常是单页路由的核心:根据新的状态更新 UI 组件
});
四、 实战:为什么 Vue/React Router 需要它?
在 History 模式下,路由系统会拦截链接的默认点击行为,改为调用 history.pushState:
- 用户点击链接 ➜ 拦截默认跳转 ➜ 执行
pushState('/new-path')。 - 地址栏更新 ➜ 页面不刷新。
- 代码主动更新组件 ➜ 渲染新内容。
- 用户点击后退 ➜ 触发
popstate➜ 路由监听到变化 ➜ 切换回旧组件。
五、 面试模拟题
Q1:history.pushState 和 location.href 有什么区别?
参考回答:
location.href会导致浏览器立即向服务器发送请求,触发整页刷新。history.pushState仅修改浏览器地址栏并增加历史记录,不会触发网络请求,也不会刷新页面,是单页应用无刷新跳转的基础。
Q2:为什么 History 模式在刷新页面时会出现 404?如何解决?
参考回答: 因为 pushState 设置的 URL 是前端虚拟的。用户直接刷新时,浏览器会向服务器请求这个不存在的物理路径。 解决方法:需要后端(Nginx/Node)配合。将所有未命中的静态资源请求统一重定向(Fallback)到 index.html,让前端路由接管后续的解析。
Q3:replaceState 的典型应用场景是什么?
参考回答: 常用于重定向或纠正 URL。例如:
- 用户登录后,从登录页跳转到个人中心,可以使用
replaceState替换掉登录页。这样用户在个人中心点击“后退”时,不会再回到登录页,而是回到登录之前的页面。 - 在搜索页修改筛选条件时,实时更新 URL 却不希望产生过多的历史记录导致用户后退困难。