history.pushState新增历史记录,replaceState覆盖当前记录;二者均不刷新页面但需同源URL,state须可序列化,title被忽略;popstate仅在后退/前进时触发,需手动初始化视图并处理直接输入URL等边界情况。

history.pushState 和 history.replaceState 怎么用
浏览器原生的 history.pushState 和 history.replaceState 是实现前端路由的核心,它们不触发页面刷新,但能改变 URL 并把状态存进历史栈。
关键区别:pushState 会新增一条历史记录(用户点后退能回到上一页),replaceState 则覆盖当前条目(后退不会回到它)。
-
state参数必须是可序列化的对象(不能含函数、dom 节点等),否则某些浏览器会静默丢弃 -
title参数目前所有主流浏览器都忽略,传空字符串或NULL即可 -
url必须是同源的相对路径或绝对路径;跨域会直接抛SecurityError
history.pushState({ page: 'dashboard' }, '', '/dashboard'); history.replaceState({ page: 'profile' }, '', '/profile?id=123');
监听 URL 变化:popstate 事件怎么可靠捕获
popstate 只在用户点击浏览器后退/前进按钮,或调用 history.back() 等 API 时触发,**不会**在 pushState/replaceState 调用时触发——这点常被误以为“没生效”。
- 必须在页面加载完成后再绑定,否则可能错过初始
popstate(如从外链直接进入带 hash 的 SPA 页面) - chrome 和 firefox 对首次加载是否触发
popstate行为不一致;稳妥做法是手动读取location.pathname初始化视图 - 事件对象的
state是之前pushState或replaceState传入的对象,若为空说明是初始加载或通过地址栏跳转
window.addEventListener('popstate', (event) => { const state = Event.state; if (state && state.page === 'dashboard') { renderDashboard(); } });
html5 History 模式 vs Hash 模式:服务端要配合什么
用 pushState 实现的路由叫 “History 模式”,URL 看起来干净(如 /user/123),但有个硬性前提:服务端必须将所有前端路由路径都 fallback 到 index.html,否则用户刷新页面会 404。
立即学习“Java免费学习笔记(深入)”;
- webpack Dev Server 可配
historyApiFallback: true;vite 默认开启 - nginx 需加配置:
try_files $uri $uri/ /index.html; - apache 要启用
mod_rewrite并写RewriteRule - 如果无法控制服务端(比如纯静态托管),就只能用 Hash 模式:
location.hash改变不发请求,也无需服务端配合,但 URL 带#(如/#user/123)
实际路由逻辑里容易漏掉的边界情况
仅靠 pushState + popstate 还不够,真实场景中几个点必须手动处理:
- 用户直接输入 URL 后回车 —— 此时不会触发
popstate,需在 js 初始化时主动解析location.pathname并渲染对应页面 - 页面内链接用
会触发完整跳转;必须改为event.preventDefault()+pushState+ 手动渲染 - 滚动复位:浏览器默认不会在
pushState后滚动到顶部,需手动window.scrollTo(0, 0)(或根据路由保留滚动位置) -
history.state在页面首次加载时为null,但后续pushState后它才反映当前项;别依赖它做初始化判断