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. 避免长任务阻塞浏览器渲染进程,拆包(减少渲染延迟时间
相关推荐
小镇程序员11 分钟前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐13 分钟前
前端图像处理(一)
前端
程序猿阿伟20 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒22 分钟前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪30 分钟前
AJAX的基本使用
前端·javascript·ajax
力透键背33 分钟前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M44 分钟前
node.js第三方Express 框架
前端·javascript·node.js·express
weiabc44 分钟前
学习electron
javascript·学习·electron
盛夏绽放1 小时前
Node.js 和 Socket.IO 实现实时通信
前端·后端·websocket·node.js
想自律的露西西★1 小时前
用el-scrollbar实现滚动条,拖动滚动条可以滚动,但是通过鼠标滑轮却无效
前端·javascript·css·vue.js·elementui·前端框架·html5