1. 引言
本节为非规范性内容。 LargestContentfulPaint API 使开发者能够了解网页的加载和渲染过程,从而优化此过程。
开发者需要一个能够与用户视觉渲染体验相关联的可靠指标。像 First Paint 和 First Contentful Paint 这样的绘制加载指标侧重于初始渲染,但没有考虑被绘制内容的重要性,因此可能指示用户仍然认为页面无用的时间。
最大内容绘制(LCP)旨在成为一个页面加载指标:
-
比 First Paint 和 First Contentful Paint 更能反映用户体验
-
易于理解和推理
-
减少作弊的可能性
页面加载过程中最大的绘制通常代表了用户视角下的一个重要事件,因此我们希望默认向开发者公开该事件,使性能团队、分析提供商和实验室测量工具能够收集这些指标,无需内容创建者额外标注。
该 API 依赖于 [PAINT-TIMING] 中定义的概念,可以视为该高级特性构建在其之上的低级原语。对于内容创建者愿意标注其内容并指明页面加载周期重要节点的情况,[ELEMENT-TIMING] API 能为其提供更多可控性。
注意:Largest Contentful Paint API 只会公开符合计时条件的元素。与 Element Timing 不同,无需对元素进行标注即可让其参与 Largest Contentful Paint 的评测。
1.1. 最大内容
此 API 所用的算法持续追踪迄今为止出现过的内容,每当发现新的最大内容时即为其创建新条目。被移除的内容算法仍会考虑。特别地,如果被移除的是最大内容,只有在添加了更大内容时才会新建条目。当发生滚动或输入事件时(通常会引入新内容),算法即终止。
1.2. 用法示例
下例展示了一张图片和大量文本。开发者注册了一个观察者,在页面加载期间获取最大绘制候选条目。
< img src = "large_image.jpg" > < p id = 'large-paragraph' > This is large body of text.</ p > ...< script > const observer= new PerformanceObserver(( list) => { let perfEntries= list. getEntries(); let lastEntry= perfEntries[ perfEntries. length- 1 ]; // Process the latest candidate for largest contentful paint }); observer. observe({ entryTypes: [ 'largest-contentful-paint' ]}); </ script >
1.3. 限制
LargestContentfulPaint API 基于启发式,因此易受误差影响,存在以下问题:
-
检测到某些类型的用户输入时算法会停止。但如果用户输入发生在主内容展示前,就捕获不到主内容。实际上,如果用户输入发生得很早,算法可能产出无意义结果甚至没有结果。
-
为支持图片轮播,即使内容被移除仍认为其是最大内容。这会给使用大内容占位的启动页带来问题。
2. 术语
最大内容绘制候选是包含以下成员的结构体:
若最大内容绘制候选 candidate 满足如下条件,则有资格成为最大内容绘制:
-
candidate的element的不透明度 > 0
-
candidate的element为文本节点,或 candidate的request的响应内容字节长度 >= candidate的element的有效视觉尺寸 * 0.004
注意: 该启发式用于检测图像资源是否具备足够数据,从而被用户视为“内容丰富”。它比较实际传输的文件大小与经过解码和缩放后实际生成的像素数。那些用非常少字节编码了大量像素的图片通常是低内容背景、渐变等,不视为最大内容绘制候选。
3. 最大内容绘制
最大内容绘制涉及以下新接口:
3.1. LargestContentfulPaint
接口
[Exposed =Window ]interface :LargestContentfulPaint PerformanceEntry {readonly attribute DOMHighResTimeStamp ;loadTime readonly attribute DOMHighResTimeStamp ;renderTime readonly attribute unsigned long ;size readonly attribute DOMString ;id readonly attribute DOMString ;url readonly attribute Element ?; [element Default ]object (); };toJSON LargestContentfulPaint includes PaintTimingMixin ;
每个LargestContentfulPaint
对象都关联如下概念:
-
size,初始为 0。
-
loadTime,初始为 0。
-
id,初始为空字符串。
-
url,初始为空字符串。
-
element,包含关联的
Element,初始为。null
entryType
属性的 getter 必须返回DOMString
。
name
属性的 getter 必须返回空字符串。
startTime
属性的 getter 必须返回this 的renderTime
值(若非0),否则返回this的loadTime 值。
duration
属性的 getter 必须返回 0。
renderTime
属性必须返回给定默认绘制时间戳的this的绘制时间信息。
loadTime
属性必须返回this 的loadTime。
element
属性的 getter 必须执行以下步骤:
注意: 上述算法规定,若元素已不再是Document的派生树节点,则element
属性的 getter 不再返回该元素,包括 Shadow DOM 内的元素。
本规范还通过为Document
增加最大内容绘制尺寸(初始值为 0)的概念进行扩展。同时增加了相关的内容集合,其初值为空集合。内容集合将以 (Element,
Request)
元组
方式填充。该机制用于性能,保证算法只对每份内容处理一次。
注意: 用户代理需要维护内容集合,避免已移除内容造成内存泄漏。可使用弱指针来关联元素的生命周期,
撤除元素后延迟清理相关元组。因集合未对开发者暴露,因此不会暴露垃圾回收时机。
4. 处理模型
每个Window
具备已触发滚动事件,布尔值,初始为 false。
4.1. 对 DOM 规范的修改
本节将在 [DOM] 规范完成修改后移除。
在第1步之后添加如下步骤:
4.2. 报告最大内容绘制
Document
document、绘制时间信息 paintTimingInfo、有序集合的等待绘制图像记录 paintedImages 以及 有序集合的元素 paintedTextNodes,执行如下步骤:
-
对每个 record 属于 paintedImages:
-
令imageElement为 record 的element。
-
若imageElement未在 document 下对绘制计时透明暴露,则继续。
-
令request为record的request。
-
令 candidate 为 (imageElement, request)
-
令 intersectionRect 为交集矩形算法以 imageElement 为目标、以 viewport 为根返回的值。
-
可能添加 LargestContentfulPaint 条目,带 candidate、intersectionRect、paintTimingInfo、record 的loadTime 和 document。
-
-
对每个 textNode 属于 paintedTextNodes,
-
若textNode未在document下对绘制计时透明暴露,则继续。
-
-
若textNode的text-shadow值为none,且textNode的stroke-color值为透明,以及 textNode 的 stroke-image值为none,则继续。
-
-
令 candidate 为 (textNode, null)
-
令 intersectionRect 为空矩形。
-
对每个
Text节点 text 属于 textNode 的所拥有文本节点集合:-
扩展 intersectionRect 为包含 text 和 intersectionRect 边框盒的最小矩形。
-
-
将 intersectionRect 与可视 viewport 做交集。
-
可能添加 LargestContentfulPaint 条目,带 candidate、intersectionRect、paintTimingInfo、0 和 document。
-
4.3. 确定元素的有效视觉尺寸
- 输入
-
intersectionRect,一个
DOMRectReadOnlyimageRequest,一个
Requestelement,一个元素
document,一个文档
- 输出
-
要汇报为最大内容绘制的尺寸(像素),如果该元素不应成为LCP候选则为null。
-
令width为intersectionRect的
width。 -
令height为intersectionRect的
height。 -
令size等于
width。* height -
令rootWidth为root的可视视口的宽度(不包括任何滚动条)。
-
令rootHeight为root的可视视口的高度(不包括任何滚动条)。
-
如果size等于rootWidth乘rootHeight,返回null。
-
如果imageRequest不有资格成为最大内容绘制,返回null。
-
如果imageRequest不为null,则执行以下步骤以校正图片位置和放大:
-
令concreteDimensions为imageRequest在element中的具体对象尺寸。
-
令visibleDimensions为调整定位后的concreteDimensions,通过object-position或background-position和element的内容盒。
注意: 其中某些算法在CSS中未严格定义。预期结果是获得element中图片实际位置和尺寸,返回
DOMRectReadOnly。-
令clientContentRect为应用element的变换后的visibleDimensions最小
DOMRectReadOnly。 -
令intersectingClientContentRect为clientContentRect与intersectionRect的交集。
-
设size为
intersectingClientContentRect的。width* intersectingClientContentRect的height
注意: 此举确保仅与图片自身(不含元素装饰)求交。
-
-
返回size。
-
4.4. 可能添加 LargestContentfulPaint 条目
注意: 实现最大内容绘制API的用户代理需为在supportedEntryTypes中,在Window上下文下包含该项。
这样有助于开发者检测对该API的支持。
为了可能添加一个LargestContentfulPaint
条目,用户代理必须执行以下步骤:
- 输入
-
candidate,一个最大内容绘制候选
intersectionRect,一个
DOMRectReadOnlypaintTimingInfo,一个绘制计时信息
loadTime,一个DOMHighResTimestamp
document,一个文档
- 输出
-
无
-
令window为document的相关全局对象。
-
如果size小于等于document的最大内容绘制尺寸,返回。
-
令url为空字符串。
-
令contentInfo为一个映射,使contentInfo["size"] = size,contentInfo["url"] = url,contentInfo["id"] = id,contentInfo["loadTime"] = loadTime,contentInfo["element"] = candidate的element。
-
创建LargestContentfulPaint条目,输入为contentInfo、paintTimingInfo和document。
4.5. 创建 LargestContentfulPaint 条目
为了创建一个LargestContentfulPaint
条目,用户代理必须执行以下步骤:
- 输入
-
contentInfo,一个映射
paintTimingInfo,一个绘制计时信息
document,一个
文档 - 输出
-
无
-
将document的最大内容绘制尺寸设为contentInfo["size"]。
-
令entry为新的
LargestContentfulPaint条目,其使用document的相关realm,paint timing info为paintTimingInfo,并按如下设值: -
队列该PerformanceEntry entry。
-
5. 安全与隐私注意事项
该API底层依赖Paint Timing。与类似的Element Timing API不同,若某些元素尺寸尽管较小但在当前为“最大”,LCP仍可能暴露其计时细节。但这不会泄露比Element Timing已暴露更多的敏感信息。