LCP简介
LCP(Largest Contentful Paint)
: LCP是指页面上最大的可见内容元素(通常是图片,视频或文本)加载完成所需要的时间。它主要考虑网页中最重要的元素,因为这些元素对于用户体验影响最大。通常,LCP小于2.5秒被认为是优秀的,并被视为网站性能良好的重要标志
LCP的时间细分
1. 首字节时间 (TTFB)
也就是从浏览器发送请求 到接收到服务器返回第一个字节的时间。也是页面入口html页面的TTFB时间。
2. 资源加载延迟
TTFB 与浏览器开始加载 LCP 资源之间的间隔时间。
举个例子,LCP资源在js中append进页面,浏览器无法在解析html时进行渲染,需要执行到对应的js代码后才会开始渲染相关元素。
同理还存在:
- 使用 JavaScript 动态添加到网页的
<img>
。 - 使用会隐藏
src
或srcset
属性(通常为data-src
或data-srcset
)的 JavaScript 库延迟加载的任何元素。 - 任何需要 CSS 背景图片的元素。
3. 资源加载时间
加载 LCP 资源本身所需的时间。如果 LCP 元素不需要加载资源即可渲染,则此时间为 0。
例如:LCP资源为图片,这段时间就是图片资源加载的时间。
4. 元素渲染延迟
从 LCP 资源加载完毕到 LCP 元素完全呈现所经过的时间。
例如:
- 由于
<head>
中的样式表或同步脚本仍在加载,整个网页已被阻止呈现。 - LCP 资源已加载完毕,但 LCP 元素尚未添加到 DOM 中,原因是它正在等待 JavaScript 代码加载。
- 相应元素被其他一些代码隐藏,例如尚未决定将用户放入哪个实验组的 A/B 测试库。
- 主线程因长时间运行的任务而阻塞,渲染工作必须等待这些长时间运行的任务完成。
测量LCP
可以使用PerfromanceObserve
进行监听
作为要观测的性能指标名,可以通过PerformanceObserver.supportedEntryTypes
获取,每个字符串代表一个性能指标。
这里使用largest-contentful-paint监听
js
const LCP_SUB_PARTS = [
'Time to first byte',
'Resource load delay',
'Resource load time',
'Element render delay',
];
new PerformanceObserver((list) => {
const lcpEntry = list.getEntries().at(-1);
const navEntry = performance.getEntriesByType('navigation')[0];
const lcpResEntry = performance
.getEntriesByType('resource')
.filter((e) => e.name === lcpEntry.url)[0];
// Ignore LCP entries that aren't images to reduce DevTools noise.
// Comment this line out if you want to include text entries.
if (!lcpEntry.url) return;
// Compute the start and end times of each LCP sub-part.
// WARNING! If your LCP resource is loaded cross-origin, make sure to add
// the `Timing-Allow-Origin` (TAO) header to get the most accurate results.
const ttfb = navEntry.responseStart;
const lcpRequestStart = Math.max(
ttfb,
// Prefer `requestStart` (if TOA is set), otherwise use `startTime`.
lcpResEntry ? lcpResEntry.requestStart || lcpResEntry.startTime : 0
);
const lcpResponseEnd = Math.max(
lcpRequestStart,
lcpResEntry ? lcpResEntry.responseEnd : 0
);
const lcpRenderTime = Math.max(
lcpResponseEnd,
// Use LCP startTime (the final LCP time) because there are sometimes
// slight differences between loadTime/renderTime and startTime
// due to rounding precision.
lcpEntry ? lcpEntry.startTime : 0
);
// Clear previous measures before making new ones.
// Note: due to a bug, this doesn't work in Chrome DevTools.
LCP_SUB_PARTS.forEach((part) => performance.clearMeasures(part));
// Create measures for each LCP sub-part for easier
// visualization in the Chrome DevTools Performance panel.
const lcpSubPartMeasures = [
performance.measure(LCP_SUB_PARTS[0], {
start: 0,
end: ttfb,
}),
performance.measure(LCP_SUB_PARTS[1], {
start: ttfb,
end: lcpRequestStart,
}),
performance.measure(LCP_SUB_PARTS[2], {
start: lcpRequestStart,
end: lcpResponseEnd,
}),
performance.measure(LCP_SUB_PARTS[3], {
start: lcpResponseEnd,
end: lcpRenderTime,
}),
];
// Log helpful debug information to the console.
console.log('LCP value: ', lcpRenderTime);
console.log('LCP element: ', lcpEntry.element, lcpEntry.url);
console.table(
lcpSubPartMeasures.map((measure) => ({
'LCP sub-part': measure.name,
'Time (ms)': measure.duration,
'% of LCP': `${
Math.round((1000 * measure.duration) / lcpRenderTime) / 10
}%`,
}))
);
}).observe({type: 'largest-contentful-paint', buffered: true});
下面用几个实例来说明LCP的各个耗时步骤
初始demo
js
// 页面只有一张图片
// html
<img id="img" src="./test.jpg" style="width: 300px; height: 300px">
得到如下结果
资源加载延迟时间
代码如下
js
// js
a = 0
for(let i = 0; i < 99999999; i++) {
a += i;
}
const imgDom = document.createElement("img");
imgDom.setAttribute("id", "img3");
imgDom.setAttribute("src", "./test.jpg");
imgDom.setAttribute("style", "width: 300px; height: 300px");
document.getElementById("container").appendChild(imgDom)
在页面中模拟一段耗时逻辑,在耗时逻辑后加载图片。再次测试,得到如下结果
发现资源加载延迟
时间延长
资源加载时间
开启页面slow 3G, 模拟低网速,再次加载原始demo
得到以下结果:
就是这两段时间:
资源加载时间
就是这个图片资源下载的时间。
元素渲染延迟
代码如下:
js
// html
<img id="img" src="./test.jpg" style="width: 300px; height: 300px">
// js
a = 0
for(let i = 0; i < 99999999; i++) {
a += i;
}
得到如下结果:
注意渲染延迟
和加载延迟
的区别。加载延迟
是js还未开始加载资源的这段时间(例如先执行耗时逻辑,在append(dom)),渲染延迟
是html已经加载了相关资源(html已经解析并加载了),但是被js长任务阻塞或者未显示(比如display:none)导致的耗时。
LCP优化
- 对LCP资源进行预加载(减少
资源加载延迟时间
) - CDN,缓存,LCP资源压缩(减少
资源资源加载时间
) - 避免长任务阻塞浏览器渲染进程,拆包(减少
渲染延迟时间
)