Home Blogs 为Lighthouse v6.0优化您的网站
Applications

为Lighthouse v6.0优化您的网站

About The Author

Outline

Layer0是开源电子商务PWA框架React StoreFront的主要贡献者。 今年早些时候,我们提供了许多新功能和优化 React Storefront v7其中最重要的两个是转移到Next.js以及删除几个关键依赖项(例如MobX)以支持React的新内置功能来管理状态,例如useState钩子和上下文API。 这导致浏览器捆绑包大小大约减少一半。

这在当时是一个不错的收获,并帮助通过PageSpeed Insights (PSI)测量80年代到90年代的React Storefront典型应用程序的BUMP Lighthouse (v5.7)性能得分 。 在上下文中,在Lighthouse v5.7排名前500位的电子商务网站中,83分以上的网站表现超过99%。 我们没有意识到在接下来的几个月中减少捆绑包的重要性,此时Lighthouse v6.0将像炸弹一样掉落,并抹杀每个人的性能分数。

了解在PSI上测量的领先电子商务网站的Lighthouse分数的分布在v6.0下降时是如何改变的:

在这篇文章中,我们分享了我们如何提高React StoreFront的Lighthouse v6.0分数,但这些技术可以应用于其他框架。

此外,重要的是要注意,Google在2020年5月28日宣布, 他们将在2021年使用特定指标对网站进行排名。 将不会使用Lighthouse性能得分,尽管用于确定该得分的某些元素将会使用,即使在这种情况下,也不会使用Lighthouse等综合测试来测量,而是使用Chrome用户体验报告(Crux)中的真实现场数据来测量。

Lighthouse 6.0中的新指标:TBT,LCP和CLS

Lighthouse v6.0引入了几个新的感知速度指标,并重新定义了整体指标如何影响页面的Lighthouse性能得分。

灯塔5.7 重量 灯塔6.0 重量
First Contentful Paint (FCP) 20% First Contentful Paint (FCP) 15%
速度索引(SI) 27% 速度索引(SI) 15%
第一个有意义的画图(FMP) 7% 最大的环保涂料(LCP) 25%
第一个CPU闲置(FCI) 13% 总封锁时间(TBT) 15%
是时候交互(TTI)了 33% 是时候交互(TTI)了 15%
- - 累计布局偏移 (CLS) 5%

总封锁时间(TBT)

总阻塞时间是Lighthouse v6.0中包含的一个新指标,用于测量分析和执行JavaScript在页面加载期间阻塞主线程的时间。 此指标对现代的,重JavaScript的SPA/PWM非常苛刻。 即使React Storefront 7的捆绑包大小减少了50%,我们也看到产品页面的Lighthouse v6.0性能得分下降了20到30分,这主要是由于纳入了TBT作为影响25%总体性能得分的指标

如果您使用的是同构框架,如Next.js,它支持服务器端渲染,TBT主要由捆绑包大小和水化时间决定。 简而言之,改进TBT的唯一方法是删除依赖关系,优化组件或通过使用更少的组件简化网站。

最大内容绘制 (LCP)

LCP是一个新指标,其权重比Lighthouse v6.0的总分高出25%。 LCP的目的是通过观察最大满足元素完成绘制所需的时间来量化用户对初始页面加载性能的感知。 对于大多数网站,特别是在电子商务网站,最大的满足元素是英雄形象。 对于React Storefront应用程序中的产品页面,最大的满意元素是主产品图片。 如果您不确定这是在您的网站上哪个元素,PSI 会告诉您:

要针对LCP进行优化,必须确保尽快加载映像。

累计布局偏移 (CLS)

累积版式偏移量度量页面布局在初始页面加载期间的偏移量。 布局偏移最常见的原因是图像,图像会在从网络下载数据后调整大小以适应图像时推动元素周围的内容。 通过在加载每个图像之前为其保留空间,通常可以完全消除布局偏移。 幸运的是,React Storefront的Image组件已经做到了这一点,因此React Storefront初学者应用程序拥有一个完美的CLS分数0开箱即用。

应该注意的是,其他常见的不良CLS的罪魁祸首是横幅和弹出窗口,出现在页面最初被绘. 用户讨厌它们,现在Lighthouse也是如此。

我们如何优化React Storefront for Lighthouse v6.0

当我们使用PageSpeed Insights首次在Lighthouse v6.0上测试React Storefront starter应用程序的产品页面时 ,它的得分低至60分:

为了提高得分,我们首先将目光放在LCP上。 在2.5秒时,FCP非常高(我们将在稍后讨论),但FCP和LCP之间近3秒的差距明显是需要改进的。

优化LCP

React Storefront的主要产品图像(仅占位符-中间带有”产品1″的绿色框)呈现为HTML img标记,带有指向https://via.placeholder.com的src URL。 该网站有一个像样的TTFB (大约150毫秒),可以通过迁移到像Layer0 CDN-as-JavaScript这样的更快的CDN来改进。 尽管如此,我们仍然怀疑150毫秒是FCP和LCP之间近3秒的差距。 为了消除这种差距,我们使用类似下面的base64数据URL对图像进行内联:

				
					<img decoding="async" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAA…"/>

				
			

我们通过下载主图像,将其转换为base64并在Layer0的无服务器JavaScript云上的服务器端渲染过程中将其填充到src属性中来实现这一点。 以下是产品API端点中的行:

				
					const mainProductImage = result.product.media.full[0]
mainProductImage.src = await getBase64ForImage(mainProductImage.src)
				
			

下面是获取映像并将其转换为Base 64的代码:

				
					import fetch from 'node-fetch'

export default async function getBase64ForImage(src) {
  const res = await fetch(src)
  const contentType = res.headers.get('content-type')
  const buffer = await res.buffer()
  return `data:${contentType};base64,${buffer.toString('base64')}`
return `data:${contentType};base64,${buffer.toString('base64')}` return `data:${contentType};base64,${buffer.toString('base64')}` return `data:${contentType};base64,${buffer.toString('base64')}`
}

				
			

非常简单和古老的学校。 以及对得分的影响?

通过将LCP从5.3秒降至2.8秒,我们在该页面的Lighthouse v6.0得分中获得21分! 这样一个小的变化会显著影响Lighthouse v6.0的分数,这有点令人不安,但我们会接受。 应该注意的是,所有的指标在不同的运行之间有所不同,但总的分数是一致的在低80年代。 关于上下文,在v6.0上表现最高的领先电子商务网站得分87根据PSI衡量,看起来是直接出来的90年代-看看 www.rockauto.com

上面显示的FCP和LCP之间的差距与我们在几次试验中看到的差距一样大。 大多数情况下,间隙在100ms至300ms范围内。 有时FCP和LCP是相同的。

优化TBT

接下来,我们试图改进技术性贸易壁垒。 这是相当具有挑战性的。 如上所述,要改进LCP,您要么需要减小JavaScript包的大小,要么需要缩短补水时间。 对于大多数应用程序,要开始挖掘依赖项以缩小捆绑包,这是不可行的。 使用React Storefront 7构建的应用程序已经从许多编译时优化中受益,这些优化可最大程度地减少Next.js的webpack配置提供的捆绑包大小,以及 针对材料UI的特定Babel优化。 那么,我们在哪些方面可以改进? 水合时间。

懒惰的水龙头赢了!

幸运的是,React Storefront社区在Lighthouse v6.0发布之前已经开始支持懒水化。 这当然使我们加快了我们的努力。

在您不知道的情况下,水化是指React控制在服务器上呈现的HTML元素,以便它们可以成为交互式的。 按钮变成可点击的;旋转木马变成可滑动的,等等 一个页面包含的组件越多,水合所需的时间就越长。 复杂的组件(如主菜单和产品图像传送带)需要的时间甚至更长。

懒惰水化意味着延迟某些组件的水化,直到它是绝对必要的,最重要的是,在初始页面加载后(和TBT计算后)。 懒水可能有风险。 您必须确保页面元素在用户尝试与其交互之前已准备好响应用户输入。

在React Storefront上实施懒水化非常困难,因为Material UI依赖CSS-in-JS,后者仅在组件水合后才会向文档动态添加样式。 我会将详细信息保存到另一个时间。 最后,我们构建了一个LazyHydrate组件,开发人员可以将其插入组件树中的任何位置,以延迟水合,直到发生特定事件(例如将元素滚动到视口或用户触摸屏幕)。

以下是我们懒惰地水合显示主要商品图片的MediaCarousel的示例:

				
					 import LazyHydrate from 'react-storefront/LazyHydrate'
<LazyHydrate id="carousel" on="touch">
  <MediaCarousel
    ...
  />
</LazyHydrate> 

				
			

我们在应用的几个领域应用了懒惰水化,最显著的是:

  • 滑入式菜单:当用户点击汉堡按钮时,我们将其水化。
  • 折页下方的所有控件:包括尺寸,颜色和数量选择器以及产品信息选项卡。
  • 主图像传送带:此菜单和主菜单可能是功能最多的组件,因此水合最昂贵。

这里是Lighthouse v6.0分数与应用懒惰水化:

懒惰水化将TBT减少了近40%,并将TTI (其重量比6.0版评分高15%)减少了700毫秒。 这在整个Lighthouse v6.0得分中获得6分增益。

您会注意到FCP有所上升,但LCP下降。 这些小更改本质上是”在运行PageSpeed Insights时的噪音范围内”。 所有得分在两次跑步之间略有波动。

优化FCP

基于上述分数,我们认为FCP和/或LCP可能需要进一步改进。 我们知道脚本可以阻止呈现,因此我们研究了Next.js如何将脚本导入到文档中:

				
					<scriptsrc="/_next/static/runtime/main-cb5fd281935517c43f30.js"async=""></script>
				
			

在此处使用异步可能不是最佳选择。 如果脚本是在渲染过程中下载的,它可以在评估脚本时暂停渲染,这会增加FCP和LCP的时间。 使用defer而不是async将确保脚本仅在文档绘制完成后进行评估。

不幸的是,Next.js不允许您更改脚本的导入方式,因此我们需要如下扩展NextScript组件:

				
					import React from 'react'
import { NextScript as OriginalNextScript } from 'next/document'
/**
 * A replacement for NextScript from `next/document` that gives you greater control over how script elements are rendered.
 * This should be used in the body of `pages/_document.js` in place of `NextScript`.
 */
export default class NextScript extends OriginalNextScript {
  static propTypes = {
    /**
     * Set to `defer` to use `defer` instead of `async` when rendering script elements.
     */
    mode: PropTypes.oneOf(['async', 'defer']),
  }
  static defaultProps = {
    mode: 'async',
  }
  getScripts() {
    return super.getScripts().map(script => {
      return React.cloneElement(script, {
        key: script.props.src,
        defer: this.props.mode === 'defer' ? true : undefined,
        async: this.props.mode === 'async' ? true : undefined,
      })
    })
  }
}

				
			

然后我们将以下内容添加到pages /_ document.js中:

				
					 <NextScript mode="defer" />
				
			

令我们高兴的是,这确实提高了LCP和总分数:

它还在许多次运行中略微碰撞了FCP,但这可能在”噪音”范围内。 尽管如此,在使用Defer与异步时,总分数始终比-3高2分。

总结

当Lighthouse v6.0在2020年5月底发布时,许多网站的性能得分,包括React Storefront应用程序,都大幅下降。 在优化之前,React StoreFront初学者应用程序的PDP性能陷入了60年代的低水平。 通过这些优化,我们将其带入了90年代末的纯净空气中。 此时,我们认为进一步提高分数的唯一方法是移除依赖关系,这可能意味着以开发人员的工作效率换取应用程序性能。

这是另一个时间的讨论。 让我给您留下一些我们尝试过但不起作用的东西:

预备

Preact使捆绑包大小缩小20 -30 %,但Lighthouse v6.0的分数在所有指标(甚至是TTI)中始终较差。 我们不知道为什么,但我们知道这不是新的或独家的Lighthouse v6.0。 Lighthouse V5.7的速度也较慢。 我们将继续定期检查,并希望有一天这是固定的。

分块

next.js最近引入了更精细的浏览器资产分割。 当这在Next.js 9.1中首次引入时,我们注意到额外的较小块实际上使TTI变得更糟。 在新版本发布后,它可能会使返回用户的应用更快,因为它可以更好地利用浏览器缓存,但Lighthouse对此并不在意。 React Storefront已将浏览器块数限制为一段时间:

				
					const webpack = require('webpack')
const withReactStorefront = require('react-storefront/plugins/withReactStorefront')
module.exports = withReactStorefront({
  target: 'serverless',
  webpack: config => {
    config.plugins.push(
      new webpack.optimize.LimitChunkCountPlugin({
        maxChunks: 1,
      })
    )
    return config
  },
})

				
			

网页字体

大多数网站使用自定义Web字体。 默认情况下,React Storefront使用Roboto (但可以更改或删除)。 自定义网页字体会降低性能,简单明了。 删除网页字体,您将获得大约1秒的FCP。

与分析一样,利益相关者似乎愿意权衡性能以使用特定字体。 React Storefront从与网站相同的源站提供Web字体,以消除从第三方CDN (如Google Fonts)加载字体时可能产生的TLS协商时间。 具体来说,我们使用NPM的typefete-roboto包。 当与webpack的css-loader结合使用时,使用typefite-roboto会导致字体通过一个单独的CSS文件导入,浏览器需要下载并解析该文件。 我们认为将CSS内嵌到文档中可能有助于提高性能,但没有。

在性能方面,您始终需要进行衡量。 理论上应该有效的优化可能在实践中行不通。

提高您的Lighthouse分数

我们的客户在Lighthouse v6.0上排名前500位的电子商务网站中排名第95位。

安排咨询对话!