摘要

  • W3C 2009 年 4 月的 HTML 5 工作草案描述 pushState() 增加状态记录,以及回到已存记录时的 popstate 事件。一个文档内部的不同画面也能成为浏览器后退路径上的站点。
  • 2014 年 HTML5 正式建议把 replaceState() 与 pushState()并列,还用小游戏示范在后退时根据 event.state 重画画面。浏览器保管票根,网页负责换景。

后退按钮完全照章办事:它回到了上一条浏览记录。网页却穿着下一幕的戏服站在台上,眼神茫然。读者骂按钮坏了。浏览器拿着正确票根,网页把对应的布景弄丢了。

这种错位随着网页内部的动态切换变得明显。相册从一张照片换到下一张,搜索框给出另一批结果,地图移到别的街区,浏览器却不一定装载新文档。屏幕变了,地址仍可能原封不动。读者按后退,想找回刚才那张照片、那份结果或那个街口。如果网页没有为变化留下浏览记录,浏览器就会跳到更早的地方。网页知道自己的面板变了什么,浏览器知道用户走过哪些站;两本账得对上。

W3C 于 2009 年 4 月 23 日发布的 HTML 5 工作草案给出了连接方法。History 接口已有 back、forward、go 等熟悉动作。pushState(data, title, url)可以往会话历史中加入带状态的记录;已存的状态记录再次成为当前记录时,页面会接到 popstate。于是,同一份文档里的某个画面可以有自己的地址和状态,浏览器也能沿着这些变化往回走。

草案拿一个位置可以存成书签的小游戏作例子。角色前进一步,网页画出新的位置,地址和历史记录跟着前进。读者按后退时,浏览器取出旧站点附带的状态。游戏还得把角色画回原位。保留一张旧票根,不等于浏览器记住了场上的每一块木板。舞台经理仍是网页自己的代码。

新增和替换记录的区别,直接决定后退有多长。W3C 2014 年 10 月 28 日的 HTML5 正式建议说明,pushState()新增一站,replaceState()修改当前这一站。读者可能想回到的画面值得留下一站;眼前画面的小修订则可以留在原地。每按一个键就新建记录,后退一次会变成耐力赛。真正换了页面内部的场景却不记一笔,后退又会跨过读者刚走过的路。

2014 年文本里的线形小游戏很能说明问题。角色位置变化时,它调用 history.pushState(...)。收到 popstate 时,处理函数把 event.state 交给 setupPage(),重新布置画面。代码只有几行,分工却齐全:浏览器选中旧记录,网页把旧场景装回来。地址栏写对了而画面还是错的,就像剧院门口的海报贴到了隔壁戏台。

网页也不能任意把地址换成别人的招牌。2014 年建议为可写入的 URL 设置了边界。WHATWG 现行 HTML 标准的导航与历史章节继续规定同一文档可改写地址的条件。对 HTTP 或 HTTPS 页面来说,路径、查询参数、片段可以在许可范围内变化;网页不能靠一次方法调用就冒充另一家网站。地址应跟着画面走,不能变成假门牌。

状态数据是两种记忆之间的小纸条。浏览器把它和历史记录放在一起,网页自己决定它代表什么:被选中的标签、地图坐标、某张卡片的编号。读者返回时,popstate把旧状态递给页面。网页若不理会,地址栏和屏幕就讲起两段互不相干的故事。浏览器的档案没有失灵,是舞台经理没有听见提示音。

History API 没有把后退键的控制权交给网站。按钮仍由读者按,历史仍由浏览器走。网站决定哪次内部变化值得增加记录、给记录附上什么状态、旧记录重新活跃时画面如何恢复。这个细小的合作,让一个文档内部不断变化的网页仍能给读者一条熟悉的回头路。

2009 年草案和 2014 年正式建议展示了这份合作的两个阶段。前者已经说明网页如何加入状态记录;后者把增加与替换并列,并用实例说明如何收到历史事件后重画画面。WHATWG 的现行文本仍在更大的导航体系里规定这些经典方法。真正的历史变化不在按钮外形,而在谁记得什么:浏览器记住步骤,网页记得每一步应该长什么样。

对读者来说,期待很朴素:后退就回到刚才。History API 给动态网页一个办法,不必为每次变化重新装载文档,也能兑现这句老话。浏览器明明找对了旧记录,网页却还站在新场景里时,整台戏只有后退按钮背熟了台词。