为Core Web Vitals建立更快的网页

391 阅读9分钟

这篇文章的目的是研究前端开发人员如何建立快速的登陆页面,这是由Core Web Vitals判断的。

这些网页性能指标是谷歌搜索的一个新的排名信号,也是数字营销人员和搜索引擎优化专家所关注的。自从谷歌在2000年5月宣布这些指标以来,企业主要求前端开发人员修复缓慢的网页并建立更快的网页。

现在,Core Web Vitals已经开始推出,我去寻找最佳的前端性能模式。

什么是Core Web Vitals?

Core Web Vitals的主题包括三个以用户为中心的网页性能指标,这些指标来自真实的Chrome用户数据(CrUX)。这些分数在28天的滚动期内被汇总到第75个百分点。这三个指标是:。

  1. 最大内容绘画(LCP):网站在屏幕上显示最大元素所需的时间。它应该在2.5秒内被用户看到
  2. 首次输入延迟(FID):这是对网页响应用户输入所需时间的衡量,应该在100毫秒之内。
  3. 累积布局偏移(CLS):这是网页元素和布局在页面加载时的意外偏移,所以最好避免在加载过程中偏移页面的布局

对照最慢的连接和设备的用户来衡量你的网页是很关键的。

因此,我将我的连接节流到快速3G,并将我的CPU速度降低6倍。只有这样,我才能够浮现出谷歌搜索控制台报告的问题。

提高你的核心网络生命力得分

解决页面速度问题的秘诀是对已经拥有良好Vitals的页面进行反向工程。首先,我需要一个大多数网站都有的页面类型,因此,为了这篇文章的目的,我选择测量无处不在的定价页面,因为它们在各网站的视觉上是相似的,通常包括一个突出的行动呼吁。

然后,我用Lighthouse测试了114个移动页面,并将它们与Core Web Vitals进行了比较。这样一来,我就剩下25个快速登陆页面需要分析了。

这些网站的技术范围从Next.js到WordPress再到Ruby on Rails,有些甚至使用了jQuery。无论你(被迫)使用什么堆栈,总有办法提高你的Core Web Vital分数。

由于这些模式是利用谷歌浏览器用户体验报告(CrUX)中的数据从现有网页中逆向推导出来的,我相信如果你遵循这些模式,你将建立一个快速的网页,甚至修复一个缓慢的网页。

要想自己调查这些模式,我建议加载页面,然后查看源代码。

将后端时间保持在600ms以下

最快的10个网站的服务器响应时间从75ms到380ms不等,中位数为110ms。经验之谈?该领域的第75个百分点可能需要持续低于600ms。

域名网址TTFB
basecamp.combasecamp.com/pricing59
禾丰网www.getharvest.com/pricing322
海报ywall.comwww.postermywall.com/index.php/p…94
loom.comwww.loom.com/pricing380
梦之城_梦之城娱乐_梦之城国际娱乐_梦之城国际娱乐平台evernote.com/compare-pla…255
Trello.comtrello.com/pricing107
loom.comwww.loom.com/pricing123
shopify.comwww.shopify.com/pricing202
agilecrm.comwww.agilecrm.com/pricing118
新锐网newrelic.com/pricing76
萌芽社会.comsproutsocial.com/trial/129

计算性能预算

在查看代码之前,我建议对请求的文件进行清点,并按内容类型进行细分。然后将你的页面与一个快速页面进行比较。这就是所谓的性能预算。

我创建我的预算是通过分解每个类型的文件,在最大的内容画(LCP)之前加载。例如,Shopify加载了超过100个文件,但构建页面时,它在最大的contentful paint之前加载了17个文件。

在LCP之前,快速页面一般会加载。

  • 总文件数少于35个
  • 四个或更少的第三方脚本
  • 没有媒体文件
  • 只有两个样式表
  • 不超过10个脚本
  • 最多四个自定义字体
  • 多达15张图片

在LCP之前预先连接到要求的域

正确构建的页面首先要找到这35个文件所使用的所有域,然后使用preconnect 资源提示。Shopify使用这种模式来预连接到关键域。

<head>
      <link rel="preconnect" href="https://cdn.shopify.com" />
      <link rel="preconnect" href="https://monorail-edge.shopifysvc.com" />
      <link rel="preconnect" href="https://sessions.bugsnag.com" />
      <link rel="preconnect" href="https://www.google-analytics.com" />
      <link rel="preconnect" href="https://www.googletagmanager.com" />
      <link rel="preconnect" href="https://bat.bing.com" />
      <link rel="preconnect" href="https://www.facebook.com" />
      <link rel="preconnect" href="https://connect.facebook.net" />
      <link rel="preconnect" href="https://tags.tiqcdn.com" />
      <link rel="preconnect" href="https://lux.speedcurve.com" />
</head>

这允许浏览器在文件被请求之前打开连接。

预加载和托管字体文件

大多数网站都依赖自定义字体,并使用font-display: swap ,以便在下载自定义字体之前文本是可见的。PosterMyWall是一个模式的例子,它的字体是预先加载和自我托管的。这是加载和呈现自定义字体的最快方式。

<head>
     <link rel="preload"
href="https://www.postermywall.com/assets/fonts/NunitoSans/NunitoSans-R
egular.woff2"
     as="font" type="font/woff2" crossorigin="anonymous">
<!-- Suggested improvement →
<link rel="preload" as="style"
      href="https://pro.fontawesome.com/releases/v5.13.0/css/all.css"
      onload=”this.rel='stylesheet'">
</head>

如果你需要使用谷歌字体,你应该寻找使用Mario Ranftl的google-web-fonts-helper

避免使用图标字体

避免使用图标字体的主要原因是,它们会产生一个请求链。这意味着根文档必须加载一个样式表,而样式表在渲染图标之前又会加载图标字体文件。但是一个简单的带有高度和宽度属性的图像没有任何依赖性。

如果你需要使用图标字体,Sproutsocial在预加载字体和异步加载字体时,概述了一个有用的模式。我另外建议也要预装图标字体文件。

<head>
<link rel="preload" as="style"
      href="https://pro.fontawesome.com/releases/v5.13.0/css/all.css"
      onload=”this.rel='stylesheet'">
<!-- My recommendation -->
<link rel="preload"
            href="https://pro.fontawesome.com/releases/v5.13.0/webfonts/fa-so    lid-900.woff2"
      as="font" type="font/woff2"
crossorigin="anonymous">
</head>

我也看到了在内联中添加@fontface 规则的好处,这样可以消除对渲染的阻碍all.css

不需要内联关键的CSS样式

我惊讶地发现,许多网站都没有内联样式。相反,他们创建了一些小的CSS包,以便在<head> 中加载。例如,Loom预装的外部样式表总共约15KB。

也就是说,亚马逊AWS加载的是一个74KB的阻断渲染的CSS文件。所以,不要因为Lighthouse或某个帖子(比如这个)的推荐而使用一个模式。

<head>
  <link rel="preload" href="/_next/static/css/4971ebe5c5a60f5f989b.css"
    as="style" />
  <link rel="stylesheet" href="/_next/static/css/4971ebe5c5a60f5f989b.css"/>
  <link rel="preload" href="/_next/static/css/33a02aaca547371a75e0.css"
    as="style" />
  <link rel="stylesheet" href="/_next/static/css/33a02aaca547371a75e0.css"/>
</head>

加载小的JavaScript包

我的预算建议在最大的contentful paint之前不超过10个脚本。这不允许加载10个大规模的文件,但最好的网站在页面底部智能地应用asyncdefer 属性加载小脚本(2到35KB)。

有几个网站使用Next.js,它提供了在交互式运输JavaScript内联之前和之后加载脚本的能力,以及懒惰加载脚本的能力(对第三方供应商来说非常好)。脚本也是预加载的。

如果你在预装脚本时有不同的技术栈,要注意资源排序。

对小图片使用内联SVG,其余的懒惰加载

如果你有可以成为相对较小的SVG的图片,请内联它们。Trello将所有在折叠上方的SVG图片内联,然后懒惰地加载那些在屏幕外或隐藏的图片。

我也曾对折叠上方的图片使用过loading=eager 。像Trello这样的网站使用本地的loading=lazy 属性,但也观察到了JavaScript的解决方案。

<body>
     <svg
        height="16"
        viewBox="0 0 16 16"
        width="16"
        <path>...</path>
      </svg>
      <!-- Native -->
      <img
         src="//images.ctfassets.net/TREL-210_Trello_Access_Spot_Illo.svg"
                alt="Atlassian Access"
          width="271"
          height="190"
          loading="lazy"
                class="Picture__Image-sc" />
      <!-- Script -->
      <img
         src="https://www.postermywall.com/assets/.../placeholder-image.png”
    data-src="https://d1csarkz8obe9u.cloudfront.net/.../green-restaurant.jpg"
              
                  class="pmw-lazy" alt="Blog post" />
</body>

使用<picture> 元素来显示一个大的英雄图像

所分析的页面中没有一个包含英雄图像组件,所以我搜索并找到了Trulia,它采用了一种新的模式。首先,他们设置了一个PerformanceMark,这在我看来是一种高度的故意。

冒着过度简化的风险,我观察到,对于大型的英雄图像,开发人员使用了包含19个<source> 元素的<picture> 元素,涵盖了所有类型的使用情况。不仅图像的大小和压缩正确地适用于桌面和移动设备,而且他们利用了<picture> 的艺术指导能力。这也告诉我,用户体验团队是这个解决方案的一部分。

<picture class="Picture__PictureContainer-sc-1exw3ow-1 gteZiU">
<source
      srcSet="https://www.trulia.com/images/app-shopping/homePage/extraLarge.jpg"
      media="(min-width:993px)" />
<source
      srcSet="https://www.trulia.com/images/app-shopping/homePage/medium.jpg"
      media="(min-width:768px)" />
<source
      srcSet="https://www.trulia.com/images/app-shopping/homePage/medium.jpg"
      media="(min-width:570px)" />
<source
      srcSet="https://www.trulia.com/images/app-shopping/homePage/small.jpg"
      media="(min-width:376px)" />
<source
      srcSet="https://www.trulia.com/images/app-shopping/homePage/small.jpg"
      media="(min-width:0px)" />
<img
      loading="auto"
      decoding="auto"
      id="homepage-banner-image"
      style="background:url(src="data:image/image/png;base64,iVBOR==");
      background-size:cover"
      width="100%"
      height="100%"       
src="https://www.trulia.com/images/app-shopping/homePage/extraLarge.jpg"
alt=""
class="Picture__Image-sc-1exw3ow-0 tzLdz" />
</picture>

而且,为了模拟背景图片,他们利用position 属性,将几个<div> 元素分层,以完成该组件。其结果是惊人的。

桌面上,大的背景图片在600ms内出现,LCP在900ms内发生。然后在缓慢的移动设备上,它在1.8s出现,并在2.2s前完成。

图像绘制的秘密是巧妙的。

<img> 元素有一个内联样式设置,一个基数为64的编码,以及一个10px by 5px的JPG作为背景图片,高度和宽度设置为100%。这就是为什么它的绘画速度如此之快,并有一个很好的效果,使图像从模糊到清晰。哇。

<readyState> ,加载第三方脚本是互动的

管理第三方供应商是一个挑战,特别是当预算建议在LCP之前只加载四个脚本时。尽管这样,该企业仍然可以使用第三方供应商,因为所分析的页面平均要求完全加载103个第三方文件。因此,我着手看看最好的网页是如何处理供应商的脚本的。

最好的模式是在readyState 之前加载最多四个脚本(即标签管理器、AB测试、RUM),然后在document.readyState 之后加载剩余的complete

<head>
    <link rel="preconnect" href="https://assets.adobedtm.com" />
</head>
<body>
      <!-- Bottom of HTML →
<script src="https://assets.adobedtm.com/launch-EN.min.js" async></script>
<! -- Self-host -->
<script src="https://cdn.shopify.com/speedcurve-lux/js/lux.js?id=64441087" async defer crossorigin="anonymous"></script>
</body>

如果你采用了一个标签管理器,瀑布应该是可以预期的。它在DOMContentLoaded 之前加载关键的第三方,包括你的标签管理器脚本。然后,将浏览器的事件触发器设置为Window Loaded,用于你所有剩余的脚本。

Third Party Waterfall Displaying When Third-Party Vendors Should Be Loaded

这描述了最好的模式。然而,你的情况可能需要不那么积极的东西。启示是要控制脚本的加载时间,不仅仅是对供应商,而是对所有的脚本,以优化瀑布流。

用数据驱动的模式来改善核心网络生命力

我确信在解决LCP问题时我遗漏了一些东西,但这些案例研究强调了为什么核心网络生命力(首次输入延迟)并不能带来更好的JavaScript模式。

此外,我不会期望这些高质量的网站不能通过累积布局转移。但是,我想如果我分析的是广告支持的文章而不是定价页面_,_我会发现更多的CSS模式。

这种分析并不令人惊讶。所有这些模式都是有据可查的,除了使用图片元素来制作大的英雄图像之外。

但如果你要选择一个解决方案,我建议使用这些模式,这些模式是基于案例研究的,而不是Lighthouse列出的建议。