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. 避免长任务阻塞浏览器渲染进程,拆包(减少渲染延迟时间
相关推荐
空中海1 分钟前
03 渲染机制、性能优化与现代 React
javascript·react.js·性能优化
ChalesXavier29 分钟前
Fetch API 的基本用法
javascript
是上好佳佳佳呀38 分钟前
【前端(十三)】JavaScript 数组与字符串笔记
前端·javascript·笔记
巴沟旮旯儿38 分钟前
vite项目配置文件和打包
前端·设计模式
彩票管理中心秘书长1 小时前
Pinia 插件架构与组合式函数:如何让你的 Store 长出“超能力”
前端
彩票管理中心秘书长1 小时前
Pinia 比 Vuex 强在哪?我用同一个模块写了两种实现,你自己看
前端
yingyima1 小时前
用 Cron 加 Webhook 打通自动化工作的任督二脉
前端
JackieDYH1 小时前
CSS Flexbox 与 Grid 的默认行为-布局的底层机制
前端·css·html
彩票管理中心秘书长1 小时前
E2E测试入门:别让用户帮你点鼠标了,找个机器人替你打工吧
前端
菜蒙爱学习1 小时前
【Markdown】可用的所有 HTML 标准颜色
前端·html