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