pushState 和 replaceState 都不触发页面刷新,但前者新增历史记录、后者替换当前项;state 对象不暴露于 URL 且有大小限制;popstate 不在 push/replace 时触发,需手动处理首次加载;视图更新须自行实现,服务端需配置 fallback。
关键不是“能不能改 URL”,而是“会不会触发页面刷新”——pushState 和 replaceState 都不会刷新,但前者会在历史栈里新增一条记录,后者只替换当前条目。
常见错误是误用 replaceState 导致用户点浏览器后退键失效(比如登录跳转后想禁止返回登录页,但没考虑路由状态同步)。
pushState 适合导航:从 /home → /detail/123,用户可后退回到 /homereplaceState 适合修正:URL 是 /#login,但你想抹掉哈希、改成 /login 且不增加历史项state 对象,它会被存进历史记录,但**不会序列化到 URL 中**,所以不能依赖它传敏感数据SecurityError
popstate 只在用户点击浏览器前进/后退按钮,或调用 history.back() 等 API 时触发,**不会在 pushState/replaceState 调用时触发**。
容易踩的坑是:注册了监听但没做初始路由匹配,导致页面加载时显示空白或旧视图。
router.go(location.pathname))event.state 是 push/replace 时传入的 state 对象,但首次加载时它是 null,别直接解构popstate 回调里再调用 pushState,否则可能触发无限循环(尤其配合 React Router 等库时)popstate 触发时机有延迟,建议加防抖或结合 pageshow 补充判断调用 pu 只改 URL 和历史栈,**完全不管 DOM 或数据**。所谓“单页应用跳转”,其实是你得自己写逻辑去渲染新内容。
shState
history.pushState({ page: 'about' }, '', '/about');
// 此时页面还是原来的 HTML,你需要:
document.getElementById('app').innerHTML = 'About
';
// 或触发 Vue/React 的路由响应
vue-router 的 router.push)history.pushState,否则状态和 UI 会脱节index.html,否则用户直接访问 /detail/123 会 404history.state 是当前历史记录项绑定的状态对象,但它**只在当前 tab 生命周期内有效**,刷新后就丢失(除非你用 sessionStorage 持久化)。
典型误用:把 token 或表单草稿存在 history.state 里,结果用户刷新页面就没了。
localStorage 或服务端 sessionhistory.state 对 B tab 完全不可见console.log(history.state) 查看当前值,但上线前记得删掉