要点:Extensions API 已更新,可支持往返缓存并预加载导航。有关详情,请参阅下文。
Chrome 一直在努力提高导航速度。返回/前进缓存(Chrome 96 中已在桌面设备上推出)和推测规则(在 Chrome 103 中推出)等即时导航技术改善了返回和前进体验。在这篇博文中,我们将探讨为适应这些新工作流而对浏览器扩展程序 API 进行的更新。
了解网页类型
在引入往返缓存和预渲染之前,单个标签页只有一个活动页面。这始终是可见项。如果用户返回上一页,系统会销毁当前页面(页面 B),并完全重建历史记录中的上一页(页面 A)。扩展程序无需担心生命周期页面所在的部分,因为一个标签页只有一个页面,即处于活跃/可见状态的页面。
使用往返缓存和预渲染时,标签页和页面之间将不再存在一对一关系。现在,每个标签页实际上都存储多个页面,并且页面在状态之间进行转换,而不是被销毁和重构。
例如,某个网页在生命周期的开始阶段可能是预渲染(不可见)网页,在用户点击链接时转换为活跃(可见)网页,然后在用户导航到其他网页时存储在返回/前进缓存(不可见)中,而整个过程中该网页都不会被销毁。在本文的后面部分,我们将介绍公开的新属性,以帮助扩展程序了解网页所处的状态。
请注意,一个标签页可以包含一系列预渲染的网页(而不仅仅是一个网页)、一个活动(可见)网页,以及一系列往返缓存的网页。
扩展程序开发者将面临哪些变化?
框架 ID == 0
在 Chromium 中,我们将最顶部的/主框架称为最外层框架。
如果扩展程序作者假定最外层帧的 frameId 为 0(以前的最佳实践),则可能会遇到问题。由于标签页现在可以有多个最外层框架(预渲染的页面和缓存的页面),因此假设标签页只有一个最外层框架是不正确的。frameId == 0
仍会继续表示活动页面的最外层框架,但同一标签页中其他页面的最外层框架将不为零。添加了新字段 frameType 以解决此问题。请参阅本帖子的“如何确定某个帧是否为最外层帧?”部分。
帧生命周期与文档生命周期
扩展程序存在问题的另一个概念是帧的生命周期。框架托管一个文档(该文档与提交的网址关联)。文档可能会发生变化(例如通过导航),但 frameId 不会,因此很难仅通过 frameId 将特定文档中发生的事情相关联。我们引入了 documentId 这一概念,它是每个文档的唯一标识符。如果导航到某个框架并打开新文档,标识符将会更改。此字段对于确定页面何时更改其生命周期状态(在预渲染/活跃/缓存之间)非常有用,因为它会保持不变。
网页导航事件
chrome.webNavigation
命名空间中的事件可以在同一页面上多次触发,具体取决于其所处的生命周期。请参阅“如何确定页面处于哪个生命周期?”和“如何确定页面转换的时间?”部分。
如何判断网页处于哪个生命周期阶段?
之前提供 frameId
的许多扩展程序 API 中添加了 DocumentLifecycle
类型。如果事件中存在 DocumentLifecycle
类型(例如 onCommitted
),则其值为事件生成时的状态。您可以随时通过 WebNavigation
getFrame()
和 getAllFrames()
方法查询信息,但始终首选使用事件中的值。如果您使用这两种方法中的任一方法,请注意,在事件生成到这两种方法返回的 promise 解析完毕之间,帧的状态可能会发生变化。
DocumentLifecycle
具有以下值:
"prerender
”:目前未向用户显示,但可能准备向用户显示。"active"
:目前向用户显示。"cached"
:存储在往返缓存中。"pending_deletion"
:文件正在销毁。
如何确定某个帧是否为最外层帧?
以前,扩展程序可能会检查 frameId == 0
,以确定发生的事件是否针对最外层框架。当一个标签页中有多个页面时,我们现在有多个最外层的帧,因此 frameId 的定义会出现问题。您永远不会收到有关往返缓存帧的事件。不过,对于预渲染帧,最外层帧的 frameId
将不为零。因此,使用 frameId == 0
作为信号来确定它是否是最外层的帧不正确。
为了帮助解决此问题,我们引入了一个名为 FrameType
的新类型,因此现在可以轻松确定帧是否确实是最外层的帧。FrameType
具有以下值:
"outermost_frame"
:通常称为顶层帧。请注意,这些类型有多种。例如,如果您有预渲染且缓存的网页,则每个网页都有一个最外层框架,可以称为其最顶层框架。"fenced_frame"
:预留以备将来使用。"sub_frame"
:通常是 iframe。
我们可以将 DocumentLifecycle
与 FrameType
结合使用,并确定某个帧是否为最外层的有效帧。例如:tab.documentLifecycle === “active” && frameType === “outermost_frame”
。
如何解决相框的使用时间问题?
如上所述,帧会托管文档,并且帧可能会导航到新文档,但 frameId
不会更改。当您收到仅包含 frameId
的事件时,这会导致问题。如果您查询帧的网址,可能会发现该网址与事件发生时不同,这称为使用时间问题。
为解决此问题,我们引入了 documentId
(和 parentDocumentId
)。现在,如果提供了 documentId
,webNavigation.getFrame() 方法会使 frameId
成为可选项。每次浏览帧时,documentId
都会更改。
如何确定网页何时转换?
有明确的信号可用于确定页面何时在状态之间转换。
我们来看看 WebNavigation
事件。
对于任何网页的首次导航,您会按下列顺序看到四个事件。请注意,这 4 个事件可能会在 DocumentLifecycle
状态为 "prerender"
或 "active"
时发生。
onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted
下图展示了预渲染页面变为活动页面时 documentId
会更改为 "xyz"
。
当网页从往返缓存或预渲染状态转换为活动状态时,系统会再触发 3 个事件(但 DocumentLifecyle
为 "active"
)。
onBeforeNavigate
onCommitted
onCompleted
documentId
将保持与原始事件相同。上图说明了当 documentId
== xyz 激活时的情况。请注意,由于页面已加载,因此除了 onDOMContentLoaded
事件外,系统会触发相同的导航事件。
如果您有任何意见或问题,欢迎随时在 chromium-extensions 群组进行提问。