LCP优化

LCP简介

LCP(Largest Contentful Paint): LCP是指页面上最大的可见内容元素(通常是图片,视频或文本)加载完成所需要的时间。它主要考虑网页中最重要的元素,因为这些元素对于用户体验影响最大。通常,LCP小于2.5秒被认为是优秀的,并被视为网站性能良好的重要标志

LCP的时间细分

1. 首字节时间 (TTFB)

也就是从浏览器发送请求接收到服务器返回第一个字节的时间。也是页面入口html页面的TTFB时间。

2. 资源加载延迟

TTFB 与浏览器开始加载 LCP 资源之间的间隔时间。

举个例子,LCP资源在js中append进页面,浏览器无法在解析html时进行渲染,需要执行到对应的js代码后才会开始渲染相关元素。

同理还存在:

  • 使用 JavaScript 动态添加到网页的 <img>
  • 使用会隐藏 srcsrcset 属性(通常为 data-srcdata-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优化

  1. 对LCP资源进行预加载(减少资源加载延迟时间
  2. CDN,缓存,LCP资源压缩(减少资源资源加载时间
  3. 避免长任务阻塞浏览器渲染进程,拆包(减少渲染延迟时间
相关推荐
musk12125 分钟前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
国科安芯26 分钟前
【AS32系列MCU调试教程】SPI调试的常见问题解析
单片机·嵌入式硬件·性能优化·硬件架构·硬件工程
桦说编程28 分钟前
深入解析CompletableFuture源码实现
java·性能优化·源码
翻滚吧键盘34 分钟前
js代码09
开发语言·javascript·ecmascript
万少1 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL1 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl021 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang1 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景1 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼1 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js