用户体验差的一个因素就是从开始加载到看到任何内容的时候花费的时间太长了。FCP阐述了第一个元素加载花费了多长时间,但它并没有捕获最大内容块(通常也是最有意义的部分)加载花费了多长时间。
LCP是CWV指标中的一部分,它负责衡量视图中最大内容块可见所消耗的事件。它可以用于确定定义页面中的主要部分什么时候加载完。
image.png
LCP低的一些常规原因:

  • api请求响应慢
  • 阻塞渲染的JS和CSS
  • 资源加载过慢
  • 客户端渲染

    api请求响应慢

    从服务端收到响应的时间越长,渲染内容的时间就越长。一个快速的服务端响应可以有效的改善每个页面的加载指标,包括LCP。
    首先,改进服务器处理内容的方式。使用TTFB去衡量你的服务端响应时间。你可以通过改善你的TTFB通过以下方式:

  • 优化服务端

  • 路由用户到附近的CDN
  • 缓存静态资源
  • 缓存首屏页面的内容
  • 提前建立连接
  • 使用签名交换

    优化服务端

    您是否正在运行需要花费大量时间才能完成的昂贵查询?或者服务器端是否有其他复杂的操作会延迟返回页面内容的过程?分析服务器端代码的以及提高效率,将直接降低浏览器接收数据的时间。
    许多服务器端 Web 框架不仅需要在浏览器请求时立即提供静态页面,还需要动态创建 Web 页面。换句话说,框架不仅需要在浏览器请求时发送一个已经准备好的完整 HTML 文件,还需要运行逻辑来构建页面。这可能是由于来自数据库查询的未决结果,甚至是因为组件需要由 UI 框架(例如 React)生成标记。在服务器上运行的许多 Web 框架都有性能指导,您可以使用它们来加快此过程。
    阅读 Fix an overloaded server获得更多技巧。

    路由用户到附近的CDN

    CDN是分布在许多不同位置的服务器网络。如果您网页上的内容托管在单个服务器上,那么对于地理上较远的用户来说,您的网站加载速度会变慢,因为他们的浏览器请求实际上必须环游世界。考虑使用 CDN 来确保您的用户永远不必等待对遥远服务器的网络请求。

    缓存资源

    如果您的 HTML 是静态的并且不需要在每次请求时更改,可以使用缓存防止它重新创建。通过在磁盘上存储生成的 HTML 的副本,服务器端缓存可以减少 TTFB 并最大限度地减少资源使用。
    根据您的工具链,有许多不同的方法来应用服务器缓存:

  • 配置反向代理(Varnish、nginx)以提供缓存内容或在应用程序服务器前面安装时充当缓存服务器

  • 配置和管理您的云提供商(Firebase、AWS、Azure)的缓存行为
  • 使用 CDN,以便缓存和存储您的内容更靠近您的用户

使用页面缓存

页面安装service worker后台,它会在后台运行,并且可以打断请求。编程式的缓存可以使得你可以缓存页面部分或者全部的内容,并且只更新内容更新的部分的缓存。
下面这张图将会像你阐述使用这个模式后LCP的改善情况。
image.png
有无SW的时候LCP的统计对比图 - philipwalton.com
这个图通过分段展示了LCP在过去28天的改善情况。在使用SW后,LCP有明显的改善。如果你想学习更多关于页面缓存的知识,可以阅读 Smaller HTML Payloads with Service Workers

提前建立连接

对第三方的请求也会影响到LCP,尤其是他们需要在页面上展示指定内容的时候。使rel="preconnect"
提醒浏览器你的页面将会请求这个地址,以便提前简历一个连接。

  1. <link rel="preconnect" href="https://example.com" />

你也可以使用 dns-prefetch 去提前解析DNS地址。

  1. <link rel="dns-prefetch" href="https://example.com" />

尽管两部分负责的内容不一致,但是考虑到功能降级的问题,使用dns-prefetch作为备用功能支持。关于更多请查阅 Establish network connections early to improve perceived page speed

使用签名交换 (SXGs)

签名交换 (SXG) 是一种交付机制,通过以易于缓存的格式提供内容来实现更快的用户体验。具体来说,Google 搜索会缓存并有时预取 SXG。对于从 Google 搜索获得大部分流量的网站,SXG 可以成为改进 LCP 的重要工具。有关更多信息,请参阅 Signed Exchanges

阻塞渲染的JS和CSS

在浏览器渲染内容前,它需要解析HTML生成DOM树。当它遇到样式或者同步脚本的时候,就会暂停解析DOM。脚本和样式都会阻塞渲染,降低FCP和LCP。延迟加载不必要的JS和CSS可以加速首屏渲染。

减少CSS的阻塞时间

确保在你的网站渲染的时候仅有最少数量的必要的样式加载:

  • 压缩CSS
  • 异步加载不必要的CSS
  • 内联CSS

    压缩CSS

    为了保证样式的易读性,通常样式文件包含空格,缩进和注释。这些字符在浏览器上都不是必要的,可以通过压缩确保在生产环境移除相关内容。减少样式的加载可以提高首屏渲染的速度,使得LCP获得一个更好的结果。
    如果你的项目有用一些打包工具,你可以通过打包工具提供的插件来优化CSS:

  • webpack: optimize-css-assets-webpack-plugin

  • Gulp: gulp-clean-css
  • Rollup: rollup-plugin-css-porter

image.png
压缩CSS前后的LCP的差距
更多细节查看 Minify CSS

异步加载不必要的CSS

使用开发者工具中的 Coverage 标签下的功能可以分析你在页面中没有使用到的样式.
image.png
如何优化:

  • 移除不必要的样式或者迁移到需要使用的页面上
  • 如果不是首屏渲染需要的样式,可以使用 loadCSS 异步加载

image.png异步加载不必要的样式后LCP的差异
更多细节阅读 Defer non-critical CSS .

内联关键的样式

将关键的样式内联在head处:
image.png
内联关键的样式
内联重要样式消除了进行往返请求以获取关键 CSS 的需要。推迟其余部分可以最大限度地减少 CSS 阻塞时间。
如果您无法手动向站点添加内联样式,请使用工具库来处理。如下一些例子:

image.png
LCP 改进示例:内联关键 CSS 之前和之后
查看提取 Extract critical CSS 以了解更多信息。

减少 JavaScript 阻塞时间

下载并向用户提供最少量的必要 JavaScript。减少阻塞 JavaScript 的数量会导致更快的渲染,从而获得更好的 LCP。
这可以通过以几种不同的方式优化脚本来实现:

  • 缩小和压缩 JavaScript 文件
  • 推迟未使用的 JavaScript
  • 最小化未使用的 polyfill

    Optimize First Input Delay基本覆盖了所有需要改善的地方。

    资源加载过慢

    虽然增加 CSS 或 JavaScript 阻塞时间会直接导致性能下降,但加载许多其他类型资源所需的时间也会影响绘制时间。影响 LCP 的元素类型有:

  • 优化LCP - 图8

  • 中的
  • (使用封面可以改善LCP)
  • 具有通过该url()函数加载的背景图像的元素(与 CSS 渐变相反)
  • 包含文本节点或者内联文本的块级元素

如果在首屏渲染,加载这些元素所需的时间将对 LCP 产生直接影响。有几种方法可以确保尽快加载这些文件:

  • 优化和压缩图像
  • 预加载重要资源
  • 压缩文本文件
  • 在不同的网络情况请求不同的文件(文件降级,网络不好的时候请求低质量图片之类的)
  • 使用 Service Worker 缓存文件

优化和压缩图像

对于许多网站,当页面加载完毕时,图像是最大的视图元素。主图、大型轮播或横幅图片都是这种情况的常见情况。
image.png
图片作为最大页面元素:design.google
改善加载和渲染这些类型图像所需的时间将直接加快 LCP。有以下措施:

  • 首先考虑不使用图像。如果它与内容无关,请将其删除。
  • 压缩图像(例如使用Imagemin
  • 将图像转换为更新的格式(JPEG 2000、JPEG XR 或 WebP)
  • 使用响应式图片
  • 考虑使用图像 CDN

更多细节阅读 Optimize your images

预加载重要资源

有时,在某个 CSS 或 JavaScript 文件中声明或使用的重要资源可能会比您希望的更晚获取,例如隐藏在应用程序的许多 CSS 文件之一中的字体。
如果您知道应该哪些资源需要被优先考虑,请使用更快地获取它。它可以预加载多种类型的资源,但您应该首先关注预加载关键资源,例如字体、首屏图像或视频以及关键路径 CSS 或 JavaScript。

  1. <link rel="preload" as="script" href="script.js" />
  2. <link rel="preload" as="style" href="style.css" />
  3. <link rel="preload" as="image" href="img.png" />
  4. <link rel="preload" as="video" href="vid.webm" type="video/webm" />
  5. <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />

从 Chrome 73 开始,预加载可以与 responsive images 一起使用,将两种模式结合起来,以实现更快的图像加载。

  1. <link
  2. rel="preload"
  3. as="image"
  4. href="wolf.jpg"
  5. imagesrcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w"
  6. imagesizes="50vw"
  7. />

压缩文本文件

压缩算法,如Gzip和Brotli,可以显着减小在服务器和浏览器之间传输的文本文件(HTML、CSS、JavaScript)的大小。所有浏览器都有效支持 Gzip,Brotli 提供了更好的压缩结果,几乎可以在所有较新的浏览器中使用。
压缩您的资源将最大限度地减少它们的传输大小,缩短加载时间,从而缩短 LCP。

  1. 首先,检查您的服务器是否已经自动压缩文件。大多数托管平台、CDN 和反向代理服务器默认情况下都会对文件进行压缩编码,或者允许您轻松配置它们。
  2. 如果您需要修改服务器以压缩文件,请考虑使用 Brotli 而不是 gzip,因为它可以提供更好的压缩率。
  3. 选择要使用的压缩算法后,请在构建过程中提前压缩文件,而不是在浏览器请求时即时压缩。这最大限度地减少了服务器开销并防止在发出请求时出现延迟,尤其是在使用高压缩比时。

image.png
LCP 改进示例:Brotli 压缩前后
更多细节阅读Minify and compress network payloads

自适应服务

当加载首屏资源的时候,可以检查用户设备和网络状况选择性的加载资源。可以通过使用Network Information, Device MemoryHardwareConcurrency 这些api检查。
如果你有一些很大的资源或者一些首屏渲染的主要内容,你可以检查用户连接和设备来加载不同的资源。例如:如果用户是4G的时候你可以展示视频,但如果是较差的网络的时候,你应该加载图片。

  1. if (navigator.connection && navigator.connection.effectiveType) {
  2. if (navigator.connection.effectiveType === '4g') {
  3. // Load video
  4. } else {
  5. // Load image
  6. }
  7. }

以下的一些有价值的属性你可以使用:

  • navigator.connection.effectiveType: 有效连接类型
  • navigator.connection.saveData:启用/禁用数据保护程序
  • navigator.hardwareConcurrency:CPU核心数
  • navigator.deviceMemory: 设备内存

更多细节阅读Adaptive serving based on network quality.

使用 service worker 缓存文件

Service Worker 可用于许多场景,包括本文前面提到的提供较小的 HTML 响应。它们还可用于缓存任何静态资源,这些资源可以在重复请求时提供给浏览器而不是来自网络。
使用 Service Worker 预缓存关键资源可以显着减少它们的加载时间,特别是对于使用较弱连接重新加载网页(甚至离线访问)的用户。像Workbox这样的库可以使更新预缓存文件的过程比编写自定义 service worker 来自己处理更容易。
更多细节阅读 Network reliability

客户端渲染

许多站点使用客户端 JavaScript 逻辑直接在浏览器中呈现页面。React、Angular和Vue等框架和库使构建单页应用程序变得更加容易,这些应用程序完全在客户端处理所有内容。
如果您正在构建一个主要在客户端呈现的站点,那么您应该注意它在使用大型 JavaScript 类库时对 LCP 产生的影响。如果没有进行优化措施,在所有关键的 JavaScript 完成下载和执行之前,用户可能无法看到页面上的任何内容或与之交互。
在构建客户端渲染站点时,请考虑以下优化:

  • 最小化关键 JavaScript
  • 使用服务器端渲染
  • 使用预渲染

    最小化关键 JavaScript

    如果您网站上的内容只有在下载了一定数量的 JavaScript 后才变得可见或可以与之交互:尽可能减少捆绑包的大小变得更加重要。这可以通过以下方式完成:

  • 缩小 JavaScript

  • 推迟未使用的 JavaScript
  • 最小化未使用的 polyfill

更多细节阅读Reduce JavaScript blocking time

使用服务端渲染

对于主要由客户端呈现的网站,应始终首先关注最小化 JavaScript 的数量。但是,您还应该考虑结合服务器渲染来尽可能地改进 LCP。
首屏采用服务端渲染,后续在客户端渲染这种方法可以有效的改善LCP,但是也有以下一些问题:

  • 增加了业务复杂度
  • 与在客户端仅渲染静态页面相比,在服务端执行JS业务逻辑会增加TTFB
  • 虽然页面看起来可以交互,但是在所有脚本初始化完成前是不能交互的,会导致TTI更差。

使用预渲染

预渲染是一种单独的技术,它比服务器端渲染简单,并且还提供了一种改进应用程序中 LCP 的方法。无头浏览器是一种没有用户界面的浏览器,用于在构建期间生成每个路由的静态 HTML 文件。然后可以将这些文件与应用程序所需的 JavaScript 脚本一起提供给客户端。
使用预渲染,TTI 也有可能变差,但服务器响应时间不会像服务器端渲染解决方案那样受到影响,服务器端渲染解决方案仅在请求后才动态渲染每个页面。
image.png
LCP 改进示例:预渲染前后
要更深入地了解不同的服务器渲染架构,请查看 Rendering on the web

开发者工具

许多工具可用于测量和调试 LCP:

  • Lighthouse 6.0支持在实验室环境中测量 LCP。image.png
  • Chrome DevTools 中性能面板的计时部分包含一个 LCP 标记,当您将鼠标悬停在相关节点字段上时,它会显示与 LCP 关联的元素。image.png
  • Chrome User Experience Report提供真实环境下的 LCP 值