要点:Extensions API 已更新为支持返回/前进缓存,并预加载导航。有关详情,请参阅下文。
Chrome 一直在努力提高导航速度。返回/前进缓存(Chrome 96 中已在桌面设备上推出)和推测规则(在 Chrome 103 中推出)等即时导航技术改善了返回和前进体验。在本文中,我们将探讨我们对浏览器扩展程序 API 所做的更新,以适应这些新的工作流程。
了解网页类型
在引入“前进/后退”缓存和预渲染之前,单个标签页只有一个活动页面。这始终是可见项。如果用户返回上一页,系统会销毁当前页面(页面 B),并完全重建历史记录中的上一页(页面 A)。扩展程序无需担心网页处于生命周期的哪个部分,因为标签页只有一个状态,即活跃/可见状态。
使用前后浏览缓存和预渲染后,标签页和网页之间不再是一对一的关系。现在,每个标签页实际上都存储多个页面,并且页面在状态之间进行转换,而不是被销毁和重构。
例如,某个网页在生命周期的开始阶段可能是预渲染(不可见)网页,在用户点击链接时转换为活跃(可见)网页,然后在用户导航到其他网页时存储在返回/前进缓存(不可见)中,而整个过程中该网页都不会被销毁。在本文的后面部分,我们将介绍一些公开的新属性,这些属性可帮助扩展程序了解页面所处的状态。
请注意,一个标签页可以包含一系列预渲染的网页(而不仅仅是一个网页)、一个活动(可见)网页,以及一系列往返缓存的网页。
对于扩展程序开发者来说,会有哪些变化?
FrameId == 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
事件。
对于任何网页的首次导航,您会按下列顺序看到四个事件。请注意,当 DocumentLifecycle
状态为 "prerender"
或 "active"
时,可能会发生这四个事件。
onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted
下图展示了预渲染页面变为活动页面时 documentId
会更改为 "xyz"
。
当网页从往返缓存或预渲染状态转换为活动状态时,系统会再触发 3 个事件(但 DocumentLifecyle
为 "active"
)。
onBeforeNavigate
onCommitted
onCompleted
documentId
将保持与原始事件相同。如上图所示,当 documentId
== xyz 激活时,就会发生这种情况。请注意,会触发相同的导航事件,但 onDOMContentLoaded
事件除外,因为页面已加载。
如果您有任何意见或疑问,欢迎随时在 chromium-extensions 群组中提问。