Skip to content

pushState、replaceState 与 popstate ​

本节用一个明确的 state 对象观察历史条目、地址和 popstate 的关系。

html
<button id="next">下一页</button><output id="page"></output>
<script>
  const page = document.querySelector('#page')
  const render = () => { page.value = new URL(location.href).searchParams.get('page') || '1' }
  document.querySelector('#next').addEventListener('click', () => {
    history.pushState({ page: 2 }, '', '?page=2')
    render()
  })
  addEventListener('popstate', render)
  render()
</script>

点击后再后退,记录地址变化和 popstate 日志;改成 replaceState 比较历史条目数量。

规则与边界 ​

  • 平台保证、浏览器实现和业务约定必须分开;一个浏览器中的结果不能自动推广到所有宿主。
  • 每个监听器、请求、观察器、缓存和焦点操作都要有清理/取消责任;页面或组件离开后不能继续写状态。
  • 用户输入、URL、HTML、消息和持久化数据按不同信任边界处理;类型正确不代表内容可信。
  • 失败、取消、超时、权限拒绝、配额不足和不支持要有不同的可观察结果。

机制拆解 ​

pushState 和 replaceState 只改变当前文档的 URL/历史状态,不会自动触发页面加载;用户点击后退或前进时才触发 popstate。状态对象应足够小且可序列化,页面初始化仍需从当前 URL 读取一次。

可运行实验 ​

js
const render = () => console.log(location.pathname, location.search, history.state)
history.pushState({ page: 2 }, '', '?page=2')
render()
addEventListener('popstate', render)

先执行 push,再点击后退,记录 popstate;改用 replaceState 比较历史条目数量,并刷新验证初始化代码是否能恢复筛选状态。

练习 ​

  1. 点击按钮后记录 pushState、地址和 history.state,再点击后退记录 popstate。
  2. 将 pushState 改成 replaceState,比较历史条目数量。
  3. 说明刷新时为什么必须从当前 URL 初始化,而不能只依赖 state。 查看本节答案

下一步 ​

完成答案核对后继续本章下一篇,并把实验日志保存在自己的项目中。

用心记录每一步成长