history.pushState() 在历史栈新增记录,后退可返回;replaceState() 替换当前记录,后退跳过该步。两者均不刷新页面,但 state 必须为新对象引用,且 URL 需同源。

history.pushState() 和 history.replaceState() 的区别在哪?
两者都用来修改浏览器地址栏而不刷新页面,但行为关键不同:pushState() 会在历史栈中新增一条记录,用户点后退会回到上一个状态;replaceState() 则直接替换当前历史记录,后退时跳过这一步。
常见误用是该用 replaceState() 时用了 pushState(),比如表单提交成功后跳转到结果页,若允许用户后退再提交一次,可能引发重复操作。此时应优先用 replaceState() 避免冗余历史项。
-
pushState()适合导航类跳转(如点击菜单切换页面) -
replaceState()适合状态修正(如 URL 参数补全、hash 清理、登录后跳转) - 两个方法第三个参数
url必须与当前域同源,否则抛出SecurityError - 第一个参数
state对象会被序列化保存,但不参与 URL 构建,仅供popstate事件读取
如何监听浏览器前进/后退动作?
靠监听 popstate 事件。它只在用户点击前进/后退按钮、或调用 history.back() 等 API 触发历史栈变化时触发,不会因 pushState() 或 replaceState() 直接触发。
注意:首次加载页面时,即使 URL 带有 state,也不会触发 popstate;需要手动检查 history.state 做初始化渲染。
立即学习“Java免费学习笔记(深入)”;
window.addEventListener('popstate', (event) => {
const state = event.state; // 就是 pushState 传入的第一个参数
if (state && state.page) {
renderPage(state.page);
}
});
// 页面加载后也需处理初始 state
if (history.state && history.state.page) {
renderPage(history.state.page);
}
为什么用 history API 实现路由时总丢状态?
最常被忽略的是:每次调用 pushState() 或 replaceState() 时,传入的 state 对象必须是新引用,不能复用同一对象。因为浏览器内部会保留对该对象的引用,后续修改会影响历史记录中的快照。
另一个坑是未处理 URL 变化与视图更新的同步时机 —— pushState() 执行后,location.href 立即更新,但 popstate 不会立刻触发,所以不能依赖“调用完就渲染”,而应统一收口到事件监听里。
- 避免
const state = { page: 'home' }; pushState(state, ...); state.page = 'about'; - 正确做法是每次构造新对象:
pushState({ page: 'about' }, '', '/about') - 服务端未配置 fallback(如所有路径返回 index.html),导致直接访问 /about 时 404 —— 这不是 JS 问题,但常被归咎于 history API 失效
HTML5 history 模式 vs hash 模式,选哪个?
history 模式生成干净 URL(如 /user/123),但要求服务端配合;hash 模式靠 # 后内容(如 /#user/123),无需服务端支持,兼容性更好。
现代前端框架(React Router、Vue Router)默认 history 模式,但开发阶段常因本地 server 不支持而切回 hash 模式。上线前务必确认 Nginx/Apache 已配置 fallback:
# Nginx 示例:所有非静态资源请求都返回 index.html
location / {
try_files $uri $uri/ /index.html;
}如果项目要支持 IE9,只能用 hash 模式,因为 pushState 在 IE10+ 才完整支持。










