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 有一个 文本节点 子节点,表示非空 文本,并且该节点的使用不透明度大于 零。
注: 这涵盖了排版伪元素会覆盖 文本节点不透明度的情况。
-
target 有一个 background-image,它是一个有内容的图像,并且其使用的background-size 的宽度和高度值均非零。
-
target 有一个 border-image-source,它是一个有内容的 图像,并且其使用的border-image-width 具有非零值。
-
target 是一个具有已渲染后代的 SVG 元素。
当下列之一成立时,元素为可计时(timing-eligible):
-
一个元素具有一个background-image,它是一个有内容图像。
-
一个文本节点。
要计算可绘制包围矩形(paintable bounding rect),请对元素 target执行以下步骤:
-
令 boundingRect 为在 target 上运行
getBoundingClientRect()的结果。 -
返回 boundingRect。
注意:被 overflow 或 overflow 包含的元素,其可绘制包围矩形不会被裁剪,因为这两种情况下元素都可以通过滚动变为可见。
当以下条件全部满足时,元素 el 为可绘制(paintable):
-
el 正在被渲染。
-
el 的使用visibility 为
visible。 -
注: 可能存在
paintable元素 对用户不可见的情况,例如文本与其 背景颜色相同的情况。 为了计算首次 内容绘制,这些元素仍会被视为可绘制的。 -
注: 这涵盖了元素被 缩放为零大小、具有
display,或者具有: nonedisplay且其内容解析为空 矩形的情况。: contents注: 一般而言,元素如果位于视口内,或者 通过滚动或缩放可能进入视口,则它是可绘制的。
首次绘制 条目包含一个
DOMHighResTimeStamp
,用于报告用户代理在导航后首次渲染的时间。这不包括默认背景
绘制,但包括非默认背景绘制以及一个可绘制 iframe
的包围盒。这是开发者在页面加载过程中
关注的第一个关键时刻——用户代理开始渲染页面的时刻。
当以下任一条件满足时,浏览上下文 ctx 为可绘制计时(paint-timing eligible):
-
ctx 是顶级浏览上下文。
-
ctx 是嵌套浏览上下文,且用户代理已配置 ctx 上报绘制计时。
注意:这允许用户代理选择性地为部分 frame 启用绘制计时,例如主 frame,以及(如有需要)为某些子 frame 启用。例如,用户代理可决定为跨域 iframe 禁用绘制计时,因为某些场景下其计时可能泄露主 frame 信息。
3. Paint Timing 混入
本节是非规范性的。
The PaintTimingMixin
接口公开与渲染周期相关的时间戳。
它包含在扩展 PerformanceEntry
的平台对象中,
具体包括 PerformanceElementTiming、
LargestContentfulPaint、
PerformanceEventTiming、
PerformancePaintTiming
和 PerformanceLongAnimationFrameTiming。
paintTime
属性表示 更新渲染循环结束时的时间戳,
而 presentationTime
表示帧呈现给用户时的特定实现时间戳。
在 HTML 标准中从 更新渲染调用的 标记 paint timing 算法会填充这两个属性。
依赖于绘制时序的条目中的属性,比如 LargestContentfulPaint
的
renderTime
(以及它的 startTime),
通常返回默认绘制时间戳而不是具体的某个时间戳,
这样可以不受 presentationTime
这种具体实现细节的影响。
3.1. PaintTimingMixin
接口
[Exposed =Window ]interface mixin {PaintTimingMixin readonly attribute DOMHighResTimeStamp ;paintTime readonly attribute DOMHighResTimeStamp ?; };presentationTime
包含 PaintTimingMixin
接口混入的对象具有一个关联的 paint timing 信息(null 或 paint timing 信息)。
paint timing 信息 是一个 结构体。它具有以下 条目:
- 渲染更新结束时间
- 实现自定义的呈现时间
-
Null 或一个
DOMHighResTimeStamp
paintTime
属性的 getter 步骤是返回 this 的 paint timing 信息 的 渲染更新结束时间。
presentationTime
属性的 getter 步骤(如果存在)是返回 this 的 paint timing 信息 的 实现定义的呈现时间。
获取 默认 paint 时间戳 的步骤,对于 paint timing 信息 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
都有一个 关联图片请求,它是一个 图片请求或 null,初始为 null。
当某个 Element
element 的处理模型为以下类型:
HTMLImageElement、
SVGImageElement,
或 HTMLVideoElement
创建新的图片资源(例如用于显示图片或海报图片),
element 的 关联图片请求会被设置为所创建图片资源的 图片请求。
注意: 每一个从 URL 获取且
scheme 等于 "data" 的图片资源,
都有一个关联的 图片请求,它不会被抓取但仍需要被加载。此请求可以作为 Element
的 关联图片请求。
注意: 当前描述较为模糊,因为未指定具体算法。当相关处理模型更统一后,可进一步严格化。
每个 Element
都有一个 关联背景图片请求列表,初始为空数组。当 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. 记录绘制计时
-
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. 首次内容绘制
5.3.2. 标记绘制计时
-
令 paintTimingInfo 为一个新的绘制计时信息,其渲染更新结束时间为 以 document 的当前高精度时间,并给定 document 的相关全局对象。
-
令 paintedImages 为一个新的有序集合
-
令 paintedTextNodes 为一个新的有序集合
-
对于 doc 的待 渲染图像列表中的每个 record:
-
对于
Elementelement,它是 doc 的后代之一:-
如果 element 包含在 doc 的包含已渲染 文本的元素集合中,则继续。
-
如果 element 的拥有的文本节点集合为空, 则继续。
-
追加 element 到 doc 的包含已渲染 文本的元素集合中。
-
将 element 追加到 paintedTextNodes 中。
-
-
令 reportedPaints 为 document 的先前已报告的绘制集合。
-
令 frameTimingInfo 为 document 的当前帧计时信息。
-
将 document 的当前帧计时信息设置为 null。
-
令 flushPaintTimings 为以下步骤:
-
如果 reportedPaints 不包含
,且用户代理已配置为 标记首次 绘制,则报告绘制计时,并给定 document、"first-paint" 和 paintTimingInfo。"first-paint" 注意: 首次绘制不包括默认 背景绘制,但包括非默认背景绘制。
-
如果 document 应报告首次内容 绘制,则:
-
报告绘制计时,并给定 document、
和 paintTimingInfo。"first-contentful-paint"
注意: 父框架不应 感知来自其子 iframe 的绘制事件,反之亦然。这意味着 仅包含 iframe 的框架会有首次绘制(由于可绘制 iframe 的包围盒),但不会有首次内容绘制。
注意: 文档不保证会标记
或"first-paint" 。一个完全空白的文档可能永远不会标记首次绘制,而一个文档如果只包含不属于有内容的元素,则可能 永远不会标记首次内容绘制。"first-contentful-paint" -
-
报告最大内容绘制, 并给定 document、paintTimingInfo、 paintedImages 和 paintedTextNodes。
-
报告元素计时,并给定 document、paintTimingInfo、 paintedImages 和 paintedTextNodes。
-
如果 frameTimingInfo 不为 null,则将长动画帧 条目加入队列,并给定 document、frameTimingInfo 和 paintTimingInfo。
-
-
如果用户代理不支持实现定义的呈现时间,则调用 flushPaintTimings 并返回。
-
并行执行以下步骤:
-
等待至实现定义的时间,此时当前帧已呈现给 用户。
-
将 paintTimingInfo 的实现定义的 呈现时间设置为当前高精度时间, 并给定 document 的相关全局对象。
-
如果 document 的跨源 隔离能力为 false,则:
-
将 paintTimingInfo 的实现定义的 呈现时间粗化到下一个 4 毫秒的倍数,或更粗的精度。
-
等待直到当前高精度 时间等于 paintTimingInfo 的实现定义的 呈现时间。
-
-
在排入一个全局任务到性能时间线任务 源,并给定 document 的相关全局对象,以运行 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) element 是否为绘制时序公开, 给定一个 文档(Document)或 null document,请执行以下步骤:
6. 致谢
特别感谢所有贡献者,他们的技术意见和建议促使本规范不断完善。