边界与限制
支持
- 首次
install()时命中 scope 的当前业务 entry; install()后命中 scope 的history.pushState()业务记录;- 浏览器工具栏 Back;
history.back()和history.go(-1);- 受保护业务 entry 的普通刷新与连续刷新;
- 受保护 entry 上重复执行
replaceState(); - 多 Handler LIFO 栈;
- 异步 Handler、pending 去重和失效
resolve(); - 同一模块实例内幂等安装;
- Chromium、Firefox 和 WebKit。
固定代价
保护当前 entry 会让 history.length增加 1。每次命中 scope 的业务 pushState()会写入一条内部 edge 和一条业务记录,因此 history.length增加 2。 不命中 scope 的 pushState()保持原生行为。受保护 entry 上的公开 replaceState()先完成调用方要求的原生写入,再恢复 Guard 私有锚点;调用方对象、 业务字段、URL 与原生异常语义不变。
补丁和 popstate listener 常驻到 Document 卸载。内部 edge 也不会主动清理;没有 Handler 时由协调器透明跨过。
不保证
- 当前 entry 之前的既有记录不会被改写或单独保护;
- Forward 不调用 Handler,只透明跨过受管 edge;
location.hash、锚点链接等自动创建的记录;history.go(-N)等多步穿越和长按历史选择;- 同一任务中排队的多次穿越或极快的多次工具栏点击;
- 地址栏导航、关闭标签页或普通刷新以外的跨文档导航;
- iframe 联合 History;
- 安装前保存并绕过补丁调用的原生 History 方法;
- 内部穿越或 Handler pending 时执行 History 写入;
- History 写入失败后的回滚;
- IE 或仅支持 ES5 的运行环境。
相对 URL 以当前 URL 为基准交给 scope。原生 pushState()仍负责 state 克隆、 同源校验和公开写入错误。首次公开写入成功后的内部 History 写入失败不提供回滚。 用于保护的根 state 必须是 null、普通对象或数组;其中嵌套的 Map、Date、 数组、循环引用和共享引用按浏览器结构化克隆语义保留。锚点非法、版本不支持或找不 到对应 edge 时安全放行,不猜测穿越步数。