1. 简介
本节为非规范性内容。
Web 浏览器的主要作用之一是将 HTML、CSS 和图片资源转化为用户屏幕上的像素。衡量网页性能通常涉及测量完成这些任务(即将文本或图片内容渲染到屏幕)所需的时间。虽然有许多不同的方法可以利用这些计时信息来评判页面性能或加载体验,但所有这些方法本质上都依赖于统一的时间测量方式。
本文件是一个基础规范,规定了如何作为通用机制测量绘制计时(Paint Timing)。在此基础上,定义了“首次绘制(First Paint)”和“首次内容绘制(First Contentful Paint)”等指标。其它具体的绘制测量实例可能由其他文档进一步规定。
具体而言,本规范涵盖:
-
测量图片解码并准备绘制的时间
-
测量元素被绘制的时间
-
测量已绘制元素的尺寸
-
判断绘制元素是否包含可见内容
1.1. 首次绘制与首次内容绘制
“加载”并非单一时刻——它是一种体验,没有任何单一指标能够完全描述。加载体验过程中有多个时刻会影响用户对“快”或“慢”的感知。
首次绘制(FP)是这些关键时刻中的第一个,紧随其后的是首次内容绘制(FCP)。这些指标标记了浏览器渲染特定文档的时间点。对用户来说,这很重要,因为它回答了“页面是不是开始有动静了?”这一问题。
两者的主要区别在于:FP 标志着浏览器首次为特定文档渲染任何内容的时刻;而 FCP 标志着浏览器首次从 DOM 渲染出图片或文本内容的时刻。
1.2. 用法示例
const observer= new PerformanceObserver( function ( list) { const perfEntries= list. getEntries(); for ( const perfEntryof perfEntries) { // 处理条目 // 上报分析和监控 // ... } }); // 注册观察者以获取绘制计时通知 observer. observe({ entryTypes: [ "paint" ]});
2. 术语
绘制(Paint):当用户代理将渲染树转换为屏幕上的像素时,即执行了“绘制”(或“渲染”)。正式地讲,当用户代理执行了事件循环中的 更新渲染步骤时,视为已“渲染”文档。
注意:渲染管线非常复杂,时间戳应为用户代理在该管线中能够记录到的最新时间戳(尽力而为)。通常建议此 API 采用提交帧给操作系统用于显示的时间。
当以下条件全部满足时,生成内容伪元素为可绘制伪元素:
-
伪元素的used visibility 为
visible
。 -
伪元素生成了非空的box。
当以下条件全部满足时,CSS 图像 img 为有内容的图像(contentful image):
当DOMString
至少包含一个非文档空白字符的字符时,视为非空(non-empty)。
当以下任一条件满足时,元素 target 为有内容(contentful):
-
target 拥有 文本节点 子节点,且该文本节点为非空文本,且节点的used opacity 大于零。
注意:覆盖文本节点透明度的 排版伪元素 也包含在此情形。
-
target 拥有 background-image,其为有内容的图像,且其used background-size 宽高皆大于零。
-
target 为
canvas
且其 上下文模式(context mode) 不为none
。 -
target 为包含可渲染后代的 svg 元素。
当下列之一成立时,元素为可计时(timing-eligible):
要计算可绘制包围矩形(paintable bounding rect),请对元素 target执行以下步骤:
-
令 boundingRect 为在 target 上运行
getBoundingClientRect()
的结果。 -
返回 boundingRect。
注意:被 overflow
或 overflow
包含的元素,其可绘制包围矩形不会被裁剪,因为这两种情况下元素都可以通过滚动变为可见。
当以下条件全部满足时,元素 el 为可绘制(paintable):
-
el 正在被渲染。
-
el 的used visibility 为
visible
。 -
注意:某些情况下,可绘制元素可能对用户不可见,例如文本颜色与背景色相同时,这些元素在计算首次内容绘制时依然视为可绘制。
-
注意:这包括元素被缩放为零尺寸、
display
或: nonedisplay
且内容为空矩形的情况。: contents注意:一般而言,元素只要在视口内,或有可能通过滚动或缩放进入视口,都视为可绘制。
首次绘制(First paint)条目包含一个
DOMHighResTimeStamp
,表示用户代理在导航后首次渲染的时间。该时间不包括默认背景绘制,但包括非默认背景绘制及 iframe 的包围框。它是开发者关心的页面加载第一个关键时刻——即用户代理开始渲染页面的时刻。
当以下任一条件满足时,浏览上下文 ctx 为可绘制计时(paint-timing eligible):
-
ctx 是顶级浏览上下文。
-
ctx 是嵌套浏览上下文,且用户代理已配置 ctx 上报绘制计时。
注意:这允许用户代理选择性地为部分 frame 启用绘制计时,例如主 frame,以及(如有需要)为某些子 frame 启用。例如,用户代理可决定为跨域 iframe 禁用绘制计时,因为某些场景下其计时可能泄露主 frame 信息。
3. PaintTimingMixin
接口
[Exposed =Window ]interface mixin {
PaintTimingMixin readonly attribute DOMHighResTimeStamp ;
paintTime readonly attribute DOMHighResTimeStamp ?; };
presentationTime
包含 PaintTimingMixin
接口混入的对象拥有一个相关的 绘制计时信息(paint timing info)(为 null 或 绘制计时信息)。
绘制计时信息(paint timing info) 是一个 结构体(struct),包含以下项:
- 渲染更新结束时间(rendering update end time)
- 实现定义的呈现时间(implementation-defined presentation time)
-
Null 或
DOMHighResTimeStamp
paintTime
属性的 getter 步骤为返回 this 的 绘制计时信息的 渲染更新结束时间。
presentationTime
属性的 getter 步骤(如存在)为返回 this 的 绘制计时信息的 实现定义的呈现时间。
获取 默认绘制时间戳(default paint timestamp),针对 绘制计时信息 paintTimingInfo,返回 paintTimingInfo 的 实现定义的呈现时间(如不为 null),否则返回 paintTimingInfo 的 渲染更新结束时间。
4. PerformancePaintTiming
接口
[Exposed =Window ]interface :
PerformancePaintTiming PerformanceEntry { [Default ]object (); };
toJSON PerformancePaintTiming includes PaintTimingMixin ;
PerformancePaintTiming
扩展了 PerformanceEntry
接口的以下属性:
-
entryType
属性的 getter 必须返回
。"paint" -
startTime
属性的 getter 必须返回表示绘制发生时间的DOMHighResTimeStamp
。 -
duration
属性的 getter 必须返回 0。 -
当调用 toJSON 时,执行
PerformancePaintTiming
的默认 toJSON 步骤。
注意:实现 PerformancePaintTiming
的用户代理应在 全局对象的 supportedEntryTypes
中包含
,且该全局对象的浏览上下文需为可绘制计时。
这样开发者可以检测特定浏览上下文是否支持绘制计时。
5. 处理模型
5.1. 关联图片请求
每个 Element
都有一个关联图片请求(associated image request),可为 图片请求(image request) 或 null,初始值为 null。
当 Element
类型的 element 为 HTMLImageElement
、
SVGImageElement
或 HTMLVideoElement
时,若创建了新的图片资源(如用于显示图片或封面图片),则 element 的 关联图片请求被设置为该图片资源的 图片请求。
注意:每个通过 URL 且 scheme 等于
"data" 获得的图片资源,都有一个关联 图片请求,该图片请求不会被真正抓取但仍需要加载。此请求可成为 Element
的 关联图片请求。
注意:当前措辞较为宽泛,因为并未指向具体算法。待相关处理模型更加统一后,可进一步细化。
每个 Element
还有一个 关联背景图片请求(associated background image
requests)的列表,初始为空数组。当 Element
element 的样式需要新的图片资源(如用作背景图片)时,新资源创建的 图片请求会被添加到 element 的 关联背景图片请求列表中。
注意:一个 Element
可以有多个 图片请求,例如其 background-image 属性有多个值。如下例所示,一个 background-image 属性可产生多个 图片请求,每个都会被后续算法记录和上报。
<!DOCTYPE html> < style > div { background-image : url( https://images.example/background1.png ), url( https://images.example/background2.png ); } </ style > < div ></ div > < div ></ div >
5.2. 记录绘制计时
待处理图片记录(pending image record)是一个结构体,包含如下项:
-
element,一个
Element
-
request,一个 图片请求(image request)
-
loadTime,一个
DOMHighResTimeStamp
每个 Element
都有一个拥有的文本节点集合(set of
owned text nodes),为Text
节点组成的有序集合,初始为空。
每个 Document
都有一个已上报绘制集合(set of previously reported paints),为字符串的有序集合,初始为空。
每个 Document
都有一个待渲染图片列表(images
pending rendering),为待处理图片记录(pending image record)的列表,初始为空。
每个 Document
都有一个已渲染文本元素集合(set of elements with rendered text),为Element
的有序集合,初始为空。
5.2.1. 对 CSS 规范的修改
每当 Element
element 的关联背景图片请求(associated background image
requests)中的某个图片请求(image request)变为完全可用时,执行处理已加载完成图片(process an image that finished
loading)算法,参数为 element 和该图片请求。
5.2.2. 对 HTML 规范的修改
当 Element
element 的关联图片请求(associated image request)变为完全可用时,执行处理已加载完成图片算法,参数为 element 及其关联图片请求。
Text
节点 text 时,应执行以下步骤:
-
如果由于字体处于 字体阻塞期(font block period),text 不会被绘制,则返回。
-
令 element 为确定 text 包含块(containing block)的
Element
。
5.2.3. 处理已加载完成图片
5.3. 上报绘制计时
5.3.1. 首次内容绘制
判断Document document 是否应上报首次内容绘制(should report first contentful paint),执行如下步骤:
5.3.2. 标记绘制计时
当要求对 标记绘制计时,并以 文档(Document) document 作为输入时,执行如下步骤:
-
令 paintTimingInfo 为新的绘制计时信息,其 渲染更新结束时间为 document 的相关全局对象下的当前高精度时间。
-
令 paintedImages 为新的有序集合。
-
令 paintedTextNodes 为新的有序集合。
-
对 doc 的 待渲染图片列表中的每个 record:
-
令 reportedPaints 为 document 的 已上报绘制集合。
-
令 frameTimingInfo 为 document 的 当前帧计时信息。
-
将 document 的 当前帧计时信息 设为 null。
-
令 flushPaintTimings 为以下步骤:
-
如果 reportedPaints 不包含
,且用户代理已配置为标记首次绘制,则对 document、"first-paint"
和 paintTimingInfo 执行 上报绘制计时。"first-paint" 注意: 首次绘制不包括默认背景绘制,但包括非默认背景绘制。
-
如果 document 应上报首次内容绘制,则:
-
对 document、
和 paintTimingInfo 执行 上报绘制计时。"first-contentful-paint"
注意: 父 frame 不应感知子 iframe 的绘制事件,反之亦然。因此只包含 iframe 的 frame 会有首次绘制(由于 iframe 的包围框),但没有首次内容绘制。
注意: 文档不保证一定会标记
或"first-paint"
。完全空白的文档可能永远不会标记首次绘制,而只包含非有内容元素的文档也可能永远不会标记首次内容绘制。"first-contentful-paint" -
-
上报最大内容绘制(Report largest contentful paint),参数为 document、paintTimingInfo、paintedImages 和 paintedTextNodes。
-
上报元素计时(Report element timing),参数为 document、paintTimingInfo、paintedImages 和 paintedTextNodes。
-
如果 frameTimingInfo 不为 null,则以 document、frameTimingInfo 和 paintTimingInfo 参数,队列一个长动画帧条目(queue a long animation frame entry)。
-
-
如果用户代理不支持实现定义的呈现时间,调用 flushPaintTimings 并返回。
-
按并行执行以下步骤:
-
等待当前帧被显示给用户的实现定义时刻。
-
若 document 的 跨域隔离能力(cross-origin isolated capability)为 false,则:
-
在 document 的 相关全局对象上,向 性能时间线任务源(performance timeline task source)队列中添加全局任务,以运行 flushPaintTimings。
-
5.3.3. 上报绘制计时
-
创建一个新的
PerformancePaintTiming
对象 newEntry,位于 document 的相关 realm,并按如下设置其属性:-
设置 newEntry 的
name
属性为 paintType。 -
设置 newEntry 的
entryType
属性为
。"paint" -
设置 newEntry 的
startTime
属性为 默认绘制时间戳(default paint timestamp),参数为 paintTimingInfo。 -
设置 newEntry 的
duration
属性为 0。
-
-
设置 newEntry 的 绘制计时信息(paint timing info)为 paintTimingInfo。
5.4. 通用算法
5.4.1. 对绘制计时暴露
要判断 元素 element 是否对绘制计时暴露(exposed for paint timing),给定 Document 或 null document,执行如下步骤:
6. 致谢
特别感谢所有贡献者,他们的技术意见和建议促使本规范不断完善。