原文地址:https://web.dev/top-cwv-2023,在正文开始前,推荐一款前端监控脚本:shin-monitor,它能监控前端的错误、通信、打印等行为,以及计算各类性能参数,包括 FMP、LCP、FP 等。
我们 Google 希望我们的性能建议侧重于:
- 对现实世界产生最大影响的建议
- 与大多数网站相关且适用的建议
- 大多数开发人员可以实施的建议
在过去的一年里,我们花了很多时间来审核我们提出的全套性能建议,并根据上述三个标准对每一个建议进行评估(定性和定量)。
这篇文章概述了我们为提高每个核心页面指标(Core Web Vitals)的性能而提出的首要建议。
一、LCP
第一组建议是针对最大内容在可视区域内变得可见的时间(LCP),它是负载性能的衡量标准。
当今网络上所有站点中的 LCP 只有大约一半达到建议的阈值——所以让我们从这里开始。
1)确保可以从 HTML 源中发现 LCP 资源
根据 HTTP Archive 的 2022 Web Almanac,72% 的移动页面将图像作为 LCP 元素,这意味着对于大多数网站来说,要优化 LCP,需要确保这些图像可以快速加载。
许多开发人员可能不太清楚的是,加载图像所需的时间只是挑战的一部分,另一个关键部分是图像开始加载之前的时间。
事实上,在 LCP 元素是图像的页面中,39% 的图像具有无法从 HTML 文档源中发现的源 URL。
换句话说,这些 URL 在标准 HTML 属性(例如 <img src="..."> 或 <link rel="preload" href="...">)中找不到,而这些属性可以让浏览器迅速发现它们并立即开始加载。
如果页面需要等待 CSS 或 JavaScript 文件完全下载、解析和处理,然后图像才能开始加载,那么可能已经太晚了。
作为一般规则,如果您的 LCP 元素是图像,则图像的 URL 应该始终可以从 HTML 源中找到。 使之成为可能的一些技巧是:
- 使用具有 src 或 srcset 属性的 <img> 元素加载图像。
- 不要使用需要 JavaScript 才能呈现的非标准属性,如 data-src,因为那样总是会更慢。 9% 的页面在 data-src 后面遮盖了他们的 LCP 图像。
- 首选服务端呈现 (SSR) 而不是客户端呈现 (CSR)。
- 因为 SSR 意味着完整的页面元素(包括图像)存在于 HTML 源代码中。 CSR 解决方案要求在图像被发现之前运行 JavaScript。
- 如果你的图片需要从外部 CSS 或 JS 文件中引用,你仍然可以通过 <link rel="preload"> 标签将其包含在 HTML 源代码中。
- 请注意,浏览器的预加载扫描器无法发现内联样式引用的图像,因此即使在 HTML 源中找到它们,在加载其他资源时仍可能会阻止对它们的发现,因此预加载在这些情况下会有所帮助。
为了帮助你了解 LCP 图像是否存在可发现性问题,Lighthouse 将在 10.0 版(预计 2023 年 1 月)中发布新的审查器。
确保可以从 HTML 源中发现 LCP 资源可以带来可衡量的改进,并且还可以解锁额外的机会来确定资源的优先级,这是我们的下一个建议。
2)确保优先加载 LCP 资源
确保可以从 HTML 源中发现 LCP 资源是确保 LCP 资源可以及早开始加载的关键的第一步,但另一个重要步骤是确保优先加载该资源并且不会排在一堆不太重要的资源后面。
例如,即使 LCP 图像出现在 <img> 的标准属性中,但是在它之前有一堆 <script>,那么就必须在脚本加载完后,才能加载图像。
解决此问题的最简单方法是通过在加载 LCP 图像的 <img> 或 <link> 设置新的 fetchpriority="high" 属性,向浏览器提供哪些资源具有最高优先级的提示。
这指示浏览器提前加载它,而不是等待这些脚本完成。
根据 Web Almanac,只有 0.03% 的符合条件的页面正在利用这个新的 API,这意味着 Web 上的大多数网站都有很多机会以很少的工作来改进 LCP。
虽然 fetchpriority 属性目前仅在基于 Chromium 的浏览器中受支持,但此 API 是一种渐进式增强功能,其他浏览器会忽略它,因此我们强烈建议开发人员立即使用它。
对于非 Chromium 浏览器,确保 LCP 资源优先于其他资源的唯一方法是在文档的前面引用它。
再次使用之前的示例,如果你想确保 LCP 资源优先于那些脚本资源,你可以添加 <link rel="preload" > 标记在任何这些脚本之前,或者你可以将这些脚本移动到 <img> 下面的 <body> 中。
虽然这可行,但它不如使用 fetchpriority 来得优雅,因此我们希望其他浏览器尽快添加支持。
对 LCP 资源进行优先级排序的另一个重要方面是确保你不会执行任何导致它被取消优先级的操作,例如添加 loading="lazy" 属性。
今天,10% 的页面实际上在他们的 LCP 图像上设置了 loading="lazy"。 谨防图像优化解决方案不分青红皂白地将延迟加载行为应用于所有图像。
如果他们提供了一种覆盖该行为的方法,请确保将其用于 LCP 图像。 如果你不确定哪个图像将成为 LCP,请尝试使用试探法来选择一个合理的候选图像。
推迟非关键资源是另一种有效提高 LCP 资源相对优先级的方法。
例如,不支持用户界面的脚本(如分析脚本或社交小部件)可以安全地推迟到加载事件触发之后,这就能确保它们不会与其他关键资源(如 LCP 资源)竞争网络带宽了。
总而言之,你应该遵循这些最佳实践以确保尽早加载 LCP 资源并以高优先级加载:
- 将 fetchpriority="high" 添加到 LCP 图像的 <img> 标记中。
- 如果 LCP 资源是通过 <link rel="preload"> 标签加载的,请不要担心,因为你还可以对其设置 fetchpriority="high"。
- 永远不要在 LCP 图像的 <img> 标签上设置 loading="lazy"。
- 这样做会降低图像的优先级并延迟它开始加载的时间。
- 尽可能延迟非关键资源。
3)使用 CDN 优化文档和资源 TTFB
前两个建议侧重于确保尽早发现你的 LCP 资源并确定其优先级,以便它可以立即开始加载。 这个难题的最后一部分是确保初始文档响应也尽快到达。
浏览器在收到初始 HTML 文档响应的第一个字节之前无法加载任何子资源,并且发生得越早,其他一切也可以越早开始发生。
这段时间称为首字节时间 (TTFB),减少 TTFB 的最佳方法是:
- 在地理位置上尽可能靠近用户。
- 缓存该内容,以便可以快速再次提供最近请求的内容。
完成这两件事的最佳方式是使用 CDN。 CDN 将你的资源分发到遍布全球的边缘服务器,从而限制了这些资源必须通过线路传输到你的用户的距离。
CDN 通常还具有细粒度的缓存控制,可以根据你站点的需要进行定制和优化。
许多开发人员都熟悉使用 CDN 托管静态资产,但 CDN 也可以缓存 HTML 文档,即使是那些动态生成的文档。
根据 Web Almanac,只有 29% 的 HTML 文档请求是由 CDN 提供的,这意味着网站有很大的机会要求额外的节省。
配置 CDN 的一些技巧是:
- 考虑增加缓存内容的时间,例如内容总是新鲜的是否真的很重要?或者它可以过时几分钟?。
- 甚至可以考虑无限期地缓存内容,然后在您进行更新时清除缓存。
- 探索你是否可以将当前在源服务器上运行的动态逻辑移动到边缘计算(Edge Computing)。
一般来说,任何时候你都可以直接从边缘提供内容(避免访问你的原始服务器),这是性能上的胜利。
即使在你确实必须一路返回到原始服务器的情况,CDN 通常也会进行优化以更快地完成该过程,因此无论哪种方式都是双赢。
二、CLS
下一组建议是针对累积布局移动时间 (CLS) 的,它是衡量网页视觉稳定性的指标。
尽管 CLS 自 2020 年以来在网络上有了很大改进,但仍有约四分之一的网站未达到建议的阈值。
1)为从页面加载的任何内容设置明确的大小
当现有内容在其他内容完成加载后移动时,通常会发生布局偏移。 因此,缓解这种情况的主要方法是尽可能提前预留任何需要的空间。
修复由未调整大小的图像引起的布局偏移的最直接方法是显式设置宽度和高度属性(或等效的 CSS 属性)。
然而,根据 HTTP Archive,72% 的页面至少有一张未调整尺寸的图片。
如果没有明确的大小,浏览器最初将设置默认高度为 0px,并且可能会在最终加载图像并发现尺寸时导致明显的布局偏移。
同样重要的是要记住,图像并不是 CLS 的唯一影响因素。 布局偏移可能是由在页面最初呈现后加载的其他内容引起的,包括第三方广告或嵌入式视频。
aspect-ratio 属性可以帮助解决这个问题。 这是一个相对较新的 CSS 功能,允许开发人员明确提供图像和非图像元素的宽高比。
这将允许你设置动态宽度(例如基于屏幕尺寸),并让浏览器自动计算适当的高度,这与它们对具有尺寸的图像所做的方式大致相同。
有时无法知道动态内容的确切大小,因为它本质上是动态的。 然而,即使你不知道确切的大小,你仍然可以采取措施来降低布局偏移的严重程度。
设置合理的最小高度几乎总是比允许浏览器对空元素使用默认高度 0px 更好。
使用最小高度通常也是一个简单的解决方法,因为它仍然允许容器在需要时增长到最终的内容高度。
2)确保页面符合 bfcache 的条件
浏览器使用称为后退/前进缓存(简称 bfcache)的导航机制,直接从内存快照中立即加载浏览器历史记录中较早或较晚的页面。
bfcache 是一项重要的浏览器级性能优化,它完全消除了页面加载期间的布局变化,对于许多网站来说,这是大部分 CLS 发生的地方。
尽管如此,仍有大量网站不符合 bfcache 的条件,因此错过了获得大量导航的免费 Web 性能优化。
除非你的页面正在加载你不想从内存中恢复的敏感信息,否则你需要确保你的页面符合条件。
网站所有者应该检查他们的页面是否符合 bfcache 的条件,并解决他们不符合的任何原因。
Chrome 在 DevTools 中已经有一个 bfcache 测试器,今年我们计划通过执行类似测试的新 Lighthouse 审查器和一个 API 来增强这里的工具,以在该领域进行测量。
虽然我们在 CLS 部分中包含了 bfcache,但正如我们目前看到的最大收益一样,bfcache 通常也会改进其他 Core Web Vitals。
它是可用于大幅改进页面导航的众多即时导航之一。
3)避免使用诱导布局的 CSS 动画和过渡属性
布局变化的另一个常见来源是元素动画化。 例如,从顶部或底部滑入的横幅或其他通知横幅通常是 CLS 的影响因素。
当这些横幅将其他内容推开时,这就成了问题,但即使它们没有,动画化它们仍然会影响 CLS。
虽然 HTTP Archive 数据最终不能将动画与布局转换联系起来,但数据确实表明,将任何可能影响布局的 CSS 属性制成动画的页面,其 "良好 "的 CLS 的可能性比整体页面低15%。
有些属性比其他属性的 CLS 更差。 例如,动画页边距或边框宽度的页面具有“差”的 CLS,几乎是整体页面被评估为差的比率的两倍。
这也许并不奇怪,因为任何时候你转换或动画任何影响布局的 CSS 属性,它都会导致布局偏移,如果这些布局偏移不在用户交互的 500 毫秒内,那么它们将影响 CLS。
一些开发人员可能会感到惊讶的是,即使在元素超出正常文档流的情况下也是如此。
例如动画顶部或左侧的绝对定位元素将导致布局偏移,即使它们没有推动其他内容。
但是,如果不是对顶部或左侧进行动画处理,而是对 transform:translateX() 或 transform:translateY() 进行动画处理,则不会导致浏览器更新页面布局,因此不会产生任何布局变化。
长期以来,首选可以在浏览器的合成器线程上更新的 CSS 属性动画一直是一种性能最佳实践,因为它将工作转移到了 GPU 上并脱离了主线程。
除了作为一般性能最佳实践之外,它还可以帮助改进 CLS。
作为一般规则,永远不要为任何需要浏览器更新页面布局的 CSS 属性设置动画或过渡,除非你是为了响应用户点击或按键(尽管不是悬停)。
并且尽可能使用 CSS transform 属性的过渡和动画。
Lighthouse 的 Avoid non-composited animations 将在页面对可能较慢的 CSS 属性进行动画处理时发出警告。
三、FID
我们的最后一组建议是针对首次输入延迟 (FID) ,它是衡量页面对用户交互的响应能力的指标,即用户第一次与页面交互到浏览器对交互作出响应的时间。
虽然网络上的大多数网站目前在 FID 上得分都很高,但我们在过去记录了 FID 指标的缺点,我们相信网站仍有很多机会提高其对用户交互的整体响应能力。
我们新的与下一次绘制的交互 (INP) 指标可能是 FID 的继任者,下面的所有建议同样适用于 FID 和 INP。
鉴于网站在 INP 上的表现比 FID 差,尤其是在移动设备上,我们鼓励开发人员认真考虑这些响应建议,尽管 FID 是“好”的。
1)避免或中断长时间的任务
任务是指浏览器所做的任何一件不连续的工作。任务包括呈现、布局、解析以及编译和执行脚本。
当任务变成长任务(即 50 毫秒或更长)时,它们会阻止主线程快速响应用户输入。
根据 Web Almanac,有大量证据表明开发人员可以采取更多措施来避免或分解长任务。
虽然分解长任务可能不像本文中的其他建议那样省力,但它比本文未提供的其他技术省力。
虽然你应该始终努力在 JavaScript 中做尽可能少的工作,但你可以通过将长任务分解为更小的任务来帮助主线程,以便渲染更新和其他用户交互可以更快地发生。
另一种选择是考虑使用 isInputPending 和 Scheduler API。
isInputPending 是一个返回布尔值的函数,该值指示用户输入是否待处理。 如果它返回 true,就可以中断 JavaScript 的执行,让浏览器可以去处理那些用户输入。
Scheduler API 是一种更高级的方法,它允许你根据优先级系统安排工作,该系统考虑到正在完成的工作是用户可见的还是后台的。
通过分解长任务,你可以让浏览器有更多机会来处理关键的用户可见工作,例如处理交互和任何由此产生的渲染更新。
2)避免不必要的 JavaScript
毫无疑问:当今网站传输的 JavaScript 比以往任何时候都多,而且这种趋势看起来不会很快改变。
当你发送过多的 JavaScript 时,你正在创建一个任务竞争主线程注意力的环境。 这肯定会影响你网站的响应能力,尤其是在关键的启动期间。
然而,这并不是一个无法解决的问题。 你确实有一些选择:
- 使用 Chrome DevTools 中的覆盖工具来查找网站资源中未使用的代码。
- 通过减少启动期间所需资源的大小,可以确保网站花费更少的时间来解析和编译代码,从而带来更流畅的初始用户体验。
- 通过代码拆分将这些代码移动到单独的包中。
- 有时,使用覆盖工具找到的未使用代码会被标记为“未使用”,因为它在启动期间未被执行,但对于将来的某些功能来说仍然是必需的。
- 如果你使用的是标签管理器,请务必定期检查你的标签以确保它们经过优化,或者即使它们仍在使用中也是如此。
- 可以清除带有未使用代码的旧标签,以使你的标签管理器的 JavaScript 更小、更高效。
3)避免大型渲染更新
JavaScript 并不是唯一可以影响网站响应能力的因素。
渲染本身就可能是一种昂贵的工作——当发生大量渲染更新时,它们可能会干扰网站响应用户输入的能力。
优化渲染工作并不是一个简单的过程,它通常取决于你想要实现的目标。
即便如此,你还是可以做一些事情来确保你的渲染更新是合理的,并且不会蔓延到长时间的任务中:
- 避免使用 requestAnimationFrame() 进行任何非视觉工作。
- requestAnimationFrame() 调用在事件循环的渲染阶段处理,如果在此步骤中完成过多工作,渲染更新可能会延迟。
- 保持 DOM 小尺寸。
- DOM 大小和布局工作的强度相关。当渲染器必须为非常大的 DOM 更新布局时,重新计算其布局所需的工作会显着增加。
- 使用 CSS containment。
- CSS containment 依赖于 CSS contain 属性,它向浏览器发出指令,说明如何为 contain 属性设置的容器进行布局工作,甚至包括将布局范围和渲染隔离到 DOM 中的特定根目录。
- 这并不总是一个简单的过程,但通过隔离包含复杂布局的区域,可以避免为它们进行不必要的布局和渲染工作。
如果你想了解更多的优化建议,请查看以下列表: