要点:Extensions API 已更新,可支持往返缓存并预加载导航。有关详情,请参阅下文。
Chrome 一直致力于提高导航速度。返回/前进缓存(Chrome 96 中已在桌面设备上推出)和推测规则(在 Chrome 103 中推出)等即时导航技术改善了返回和前进体验。在这篇博文中,我们将探讨为适应这些新工作流而对浏览器扩展程序 API 进行的更新。
了解网页类型
在引入往返缓存和预渲染之前,单个标签页只有一个活动页面。这始终是可见项。如果用户返回前一个页面,当前页面将被销毁(页面 B),历史记录中的前一个页面将被完全重建(页面 A)。扩展程序无需担心生命周期页面所在的部分,因为一个标签页只有一个页面,即活跃/可见状态。
使用往返缓存和预渲染时,标签页和页面之间将不再存在一对一关系。现在,每个标签页实际上会存储多个页面,并且页面会在状态之间转换,而不是被销毁和重建。
例如,一个页面一开始是预渲染(不可见)的页面,并在用户点击链接时转换为活动(可见)页面,然后在用户导航到另一个页面时将其存储在往返缓存(不可见)中,这一切都不会被销毁。在本文的后面部分,我们将介绍一些公开的新属性,这些属性可帮助扩展程序了解页面所处的状态。
请注意,一个标签页可以具有一系列预渲染页面(而不仅仅是一个)、单个活动(可见)页面以及一系列往返缓存页面。
对于扩展程序开发者来说,会有哪些变化?
框架 ID == 0
在 Chromium 中,我们将最顶层/主框架称为最外层的框架。
如果扩展程序作者假设最外层帧的 frameId 为 0(之前的最佳做法),则可能会出现问题。由于一个标签页现在可以有多个最外层的帧(预渲染和缓存的网页),因此关于一个标签页只有一个最外层帧的假设是不正确的。frameId == 0
仍将继续表示活动页面的最外层帧,但同一标签页中其他页面的最外层帧将是非零值。为了解决此问题,我们添加了一个新字段 frameType。请参阅这篇博文的“如何确定某个帧是否为最外层的帧?”部分。
框架与文档的生命周期
扩展程序存在问题的另一个概念是帧的生命周期。框架托管一个文档(该文档与提交的网址关联)。文档可以更改(例如通过导航),但 frameId 不会,因此很难将特定文档中发生的情况与仅仅使用 frameIds 相关联。我们引入了 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
,确定某个帧是否为活跃最外层的帧。例如:
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 群组进行提问。