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. 术语
最大内容绘制候选项是一个包含以下成员的 结构体:
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。
4. 处理模型
每个Window
具备已触发滚动事件,布尔值,初始为 false。
4.1. 对 DOM 规范的修改
本节将在 [DOM] 规范完成修改后移除。
在第1步之后添加如下步骤:
4.2. 报告最大内容绘制
Document
document、 绘制时序信息 paintTimingInfo、一个 有序集合 的 待处理图片记录 paintedImages,以及一个 有序集合 的 元素 paintedTextNodes时,执行以下步骤:
注意: paintedImages 中每个待处理图片记录和 paintedTextNodes 中的文本元素,只会被 标记绘制时序 报告一次,且仅在该元素首次被认为可绘制(即具有透明度和可见性)且可内容化(即图片资源或阻塞字体已充分加载)的绘制时进行。
-
令 window 为 document 的 相关全局对象。
-
令 newCandidateSize 为 document 的 最大内容绘制尺寸。
-
令 newCandidate 为 null。
-
遍历 paintedImages 中的每个 record:
-
令 imageElement 为 record 的 元素。
-
如果 imageElement 在 document 下不 暴露用于绘制时序,则继续。
-
令 intersectionRect 为交集区域算法以 imageElement 为目标、视口为根返回的值。
-
令 size 为 有效视觉尺寸,以 intersectionRect 和 record 的 请求 为参数,针对 imageElement 计算。
-
如果 size 小于或等于 newCandidateSize,则继续。
-
将 newCandidateSize 设置为 size。
-
将 newCandidate 设置为新的 最大内容绘制候选项, 其 元素 为 imageElement, 尺寸 为 size, 请求 为 record 的 请求, 加载时间 为 record 的 加载时间。
-
-
遍历 paintedTextNodes 中的每个 textNode,
-
如果 textNode 在 document 下不 暴露用于绘制时序,则继续。
-
如果 textNode 的 alpha通道 值 <=0 或 透明度 值 <=0:
-
若 textNode 的 text-shadow 值为 none, 且 textNode 的 stroke-color 值为 透明,并且 textNode 的 stroke-image 值为 none,则继续。
-
-
令 size 为 textNode 的 有效视觉尺寸,参数为 intersectionRect 和 null。
-
如果 size 小于或等于 newCandidateSize,则继续。
-
将 newCandidateSize 设置为 size。
-
将 newCandidate 设置为新的 最大内容绘制候选项, 其 元素 为 textNode, 尺寸 为 size, 请求 为 null, 加载时间 为 0。
-
-
如果 newCandidate 不为 null:
-
创建 LargestContentfulPaint 条目,参数为 newCandidate、 paintTimingInfo 和 document。
-
4.3. 确定元素的有效视觉尺寸
为了确定一个 有效视觉尺寸,针对 元素, 执行以下步骤:
- 输入
-
intersectionRect,一个
DOMRectReadOnlyimageRequest,一个
Request或 nullelement,一个 元素
document,一个 文档
- 输出
-
用于 Largest Contentful Paint 报告的尺寸(像素),如果元素不应作为 LCP 候选项,则为 null。
-
令 width 为 intersectionRect 的
width。 -
令 height 为 intersectionRect 的
height。 -
令 size 为
width。* height -
令 rootWidth 为 root 的 视觉视口 的宽度(不含滚动条)。
-
令 rootHeight 为 root 的 视觉视口 的高度(不含滚动条)。
-
如果 size 等于 rootWidth 乘 rootHeight,返回 null。
-
如果 imageRequest 不为 null,则按如下步骤调整图片位置和缩放:
-
如果 imageRequest 的 响应 的内容长度(字节)小于 size * 0.004,则返回 null。
注意: 此启发式方法测试图片资源是否包含足够数据,以便用户感知到内容。它比较传输文件的大小和实际生成的像素数量(经过解码和任意图像缩放后)。以极少字节编码大量像素的图片通常是低内容背景、渐变等,不视为 LCP 候选项。
-
令 concreteDimensions 为 imageRequest 的 具体对象尺寸(在 element 内)。
-
令 visibleDimensions 为 concreteDimensions, 按 object-position, background-position, 和 element 的 内容盒进行定位调整。
注意: 部分相关算法在 CSS 中并未严格定义。预期结果是获得 element 中图像的实际位置与尺寸,以
DOMRectReadOnly表示。-
令 clientContentRect 为最小的
DOMRectReadOnly,包含 visibleDimensions 并应用 element 的 变换。 -
令 intersectingClientContentRect 为 clientContentRect 与 intersectionRect 的交集。
-
设置 size 为
intersectingClientContentRect 的。width* intersectingClientContentRect 的height
注意: 这样做确保仅与图片本身而不是元素装饰部分相交。
-
-
返回 size。
-
4.4. 创建 LargestContentfulPaint 条目
为了 创建
LargestContentfulPaint
条目,用户代理必须执行如下步骤:
- 输入
-
candidate,一个 最大内容绘制候选项
paintTimingInfo,一个 绘制时序信息
document,一个
文档 - 输出
-
无
-
令 url 为空字符串。
-
如果 candidate 的 请求 不为 null, 则将 url 设置为 candidate 的 请求 的 请求 URL。
-
令 entry 为新的
LargestContentfulPaint条目,使用 document 的 相关领域,其 绘制时序信息 为 paintTimingInfo,如下设置: -
队列 PerformanceEntry entry。
5. 安全与隐私注意事项
该API底层依赖Paint Timing。与类似的Element Timing API不同,若某些元素尺寸尽管较小但在当前为“最大”,LCP仍可能暴露其计时细节。但这不会泄露比Element Timing已暴露更多的敏感信息。