这是一篇由 Gecko 开发人员之一 Jonas Sicking 撰写的客座文章。
正如您所知,我们正在准备发布 Firefox 4。并且您
可能知道 Firefox 4 包含 历史记录 API(包括 HTML5 中定义的 pushState() 和 replaceState() 方法)。Safari 和 Chrome 也实现了此 API,但 Firefox 4 存在重要差异,我将在本文中进行说明。
几周前,有人发现了 pushState API 中一个相当大的缺陷。问题是,如果您使用 *state* 参数来调用 pushState() 或 replaceState(),并且用户稍后使用此状态重新加载页面,则在 load
事件触发之前无法访问该状态。这是因为访问该状态的唯一方法是通过 popstate
事件,而该事件直到 load
事件触发后才会触发。
这意味着对于使用 *state* 参数的页面,页面必须在不知道该状态的情况下进行渲染,并且只有在页面完全加载后才能向用户显示正确状态。
请注意,我在这里谈论的“状态”是传递给 pushState()/replaceState() 的 *state* 参数。URL(可以说,它是 pushState()/replaceState() 的更有用的参数)始终可以使用常规 API(如 document.location 和 window.location)进行访问。
为了解决此问题,与当前工作草案相比,我们在实现中进行了两处更改
- 始终通过
window.history.state
属性公开当前 *state*。这样,页面可以立即访问页面的当前状态,而无需等到第一个popstate
事件触发。 - 不要总是在
load
事件之后立即触发popstate
事件。
相反,仅在实际的会话历史记录转换期间触发它(即,当用户单击“后退”或“前进”或调用 history.back()/forward()/go() 时)。
此额外popstate
事件的全部目的是提供对页面状态的访问。但是,window.history.state
属性使此操作变得多余。我们发现,页面只是发现此事件出乎意料且容易导致错误。
第一个更改应该完全向后兼容,因为它是一个纯粹的附加更改。它不会影响现有代码,这些代码可能不使用此属性。
第二个更改是更大的问题。如果您的代码期望此事件始终触发,则可能会导致问题。缓解此更改风险的另一件事是,Safari 5 似乎误解了此问题上的工作草案,并且除非明确将 *state* 传递给 pushState()/replaceState(),否则不会触发此 popstate
。因此,基本上,只要您不使用 *state* 参数,Firefox 的行为就与 Safari 5 相同。
我们还在进行第三个更改
- 允许在页面加载时触发
popstate
。
当前的工作草案存在一个有点令人惊讶的限制,即它禁止在页面的 load
事件触发之前触发任何 popstate
事件。如果用户在页面加载过程中(例如,由于图像加载缓慢)单击了一个 pushState 支持的链接,然后按“后退”按钮,则不会触发 popstate
事件。只有在页面的 load
事件触发后,才允许第一个 popstate
触发。我们已删除此限制,并在按下“后退”或“前进”按钮或调用 history.back()/forward()/go() 时始终触发 popstate
。
我进行了一些测试,到目前为止还没有发现由于这些更改而导致的任何问题。不幸的是,由于如此晚才发现这些问题,这些更改直到 Firefox 4 RC 才会出现在 Firefox 测试版中。有一些 可用的测试版本,您可以立即使用它们进行测试。
关于 Jonas Sicking
Jonas 在网络浏览器方面已经潜心研究了十多年。他于 2000 年开始作为开源贡献者,为新开源的 mozilla 项目做出贡献。2005 年,他全职加入 mozilla,此后一直致力于 DOM 和 Web 平台的其他部分。他现在是 mozilla Web API 项目的技术负责人,也是 W3C IndexedDB 和文件 API 规范的编辑。
10 条评论