懒加载、异步CSS、优先请求如何拖累我们页面的性能?

原文链接:How layout position impacts three big web performance levers

我们帮助Shopify商家改善他们的网页性能,并发现了三个与布局位置相关的常见问题:

  • 图片的懒加载
  • 异步加载 CSS
  • 没有优先获取最大内容绘制(Largest Contentful Paint,LCP)图像

按照以往的经验来看,在Shopify主题中解决这三个问题一直很困难,因此我们开发了一些新功能来帮助解决这个问题。这篇文章将帮助您更好地理解这些性能反模式,以便您可以使用这些新功能。

如果你不了解核心网络指标和LCP的话,那你可以看看这篇文章:Key Web performance metrics in 2023

懒加载是什么?

图片懒加载是一种网络性能优化方式,它可以防止不必要的图片下载。延迟加载还可以使浏览器优先处理初始渲染所需的更重要的资源。相反,"eager"加载是浏览器遇到<img>标签时的默认行为。

在下面的例子中,我们有两种方式做懒加载

  • 第一种是使用标签原生的 loading 属性
html 复制代码
<img src="image.jpg" loading="lazy">
  • 第二种是使用懒加载库,比如lazysizes
html 复制代码
<script src="lazysizes.min.js" async=""></script>
<img data-src="image.jpg" class="lazyload" />

两种方法都使用IntersectionObserver来观察图像,当图片和当前视口接触时或即将接触时加载。这对于那些在页面下方或屏幕外隐藏的图像非常有用。但是,如果图像在加载时就在视口中,问题就出现了。浏览器只有在完成布局和绘制后才会加载文件。然后JavaScript才能计算交叉点。与默认的eager加载行为相比,这很明显会导致延迟。

图片懒加载可以防止不必要的字节下载。但是,它也可能导致图像展示的延迟,从而带来糟糕的用户体验。

如果你决定使用懒加载,你可能会思考哪种方法最好。使用原生懒加载的好处是不需要额外的JavaScript依赖。随着下一个核心网络指标(INP)的到来,你会希望让你的JavaScript的依赖尽量少,以期INP表现优秀。

目前,像lazysizes这样的工具相对于原生解决方案的主要优势是它们可以自动计算懒加载的图片的sizes属性。好消息是,这一功能已被提议并被HTML规范接受为新特性。Chrome即将发布一个开发者试用版本,而Firefox和Safari在撰写本文时也表明了积极的意图进行支持。

不幸的是,对于这两种方法,如果你想要快速加载任何图片,你都需要手动设置 size 属性。只有在绘制之后才能计算大小会导致与上述提到的延迟问题相同。我们很难将sizes属性设置的适当。在这种情况下,可以使用Responsive Image Linter Chrome扩展程序来帮助您更准确地调整它。输入任何不合适的sizes属性值,它都会为你提供一个好的值。这时候设置srcset可能不是那么合适 - 你并不需要为每个宽度都配有一个非常准确的图片。更快的方式是使用较短的宽度列表来利用缓存。

懒加载导致的性能不佳问题

在过去,主题开发人员无法确定部分的顺序或位置。这导致许多主题对所有图像应用了懒加载。结果是,现在有82%的Shopify网站对其最大内容绘制(LCP)图像进行懒加载。

Shopify网站在这个反模式中遇到了最大的困难。2023年7月,有82%的页面使用懒加载方式加载图像的最大内容绘制(LCP)元素。这些数据来自HTTPArchive,并根据Estela Franco在《懒加载LCP图像:为什么会出现这种反模式?》一文中的原始数据进行了更新。当时的数据是67%。附有更新查询和数据的电子表格。

这种性能的反模式会导致最大内容绘制(LCP)的延迟时间因设备和网络速度的不同而有所变化。在去年改进Dawn主题的测试中,将LCP图像从懒加载改为eager加载,性能提升了0.5秒。

此外,通过HTTPArchive观察整个Web时,懒加载LCP元素的Shopify网站的分布比eager加载的分布要慢。懒加载网站的中位数LCP比eager加载的网站慢1.0秒。当我们进一步观察较慢的部分时,在Distribution为75%的时候,这个差距增加到1.4秒。

什么是异步CSS加载?

异步加载CSS是一种很方便的模式,考虑到Shopify主题需要用到很多CSS。它可以用来降低可能不需要立即加载的CSS的优先级。这种模式更接近于一种技巧而不是一个特性,尽管它非常实用。代码示例如下:

html 复制代码
<link rel="stylesheet" href="my.css" media="print" onload="this.media='all'">
<noscript>
  <link rel="stylesheet" href="my.css">
</noscript>

他是怎么生效的呢?

浏览器只会为给设置了"screen"或"all"的媒体属性匹配的样式表设置最高的下载优先级。如果媒体属性设置为"print",则优先级将被设置为"最低"。其他优先级更高的文件将先下载。此外,初始渲染不会使用"print style"。然后,在加载事件触发后,JavaScript会将媒体属性更改为"all"或"screen"。此时,CSS文件的优先级变为"最高",一旦样式到达,页面将重新渲染。对于不支持的浏览器,最好在内添加

这就是整个渲染过程的简单过程:

异步CSS可以帮助我们将优先级较低的CSS放在次要位置。但是,它也会导致样式重排。每个CSS文件的加载情况以及浏览器是否可以批处理工作,都可能导致单独的样式重排。

异步CSS加载导致的性能不佳问题

我们看到许多使用异步CSS的Shopify网站导致布局偏移。这会影响到累积布局偏移 (CLS),这是另一个核心网络指标。换句话说,如果页面上方的元素需要这些样式,用户将会看到无样式内容的闪烁(FOUC)。

由于延迟的CSS而导致的样式重排也可能破坏了原本图像懒加载策略。IntersectionObserver可能会"错误地"检测到视口中的图像,然后延迟的CSS将该图像推到页面的更下方。注入的组件也可能会产生这种情况。其结果可能是即使最终页面布局只需要下载少量图像,但许多图像仍会一次性加载。

什么是优先请求?

Fetch优先级是一个更细致的概念。网站显示所需的文件数量很多,因此在下载队列中更智能地排序文件可以提供更快的用户体验。浏览器根据不同的文件类型和上下文设置下载优先级。例如,浏览器将CSS文件设置为最高优先级,因为它们会阻塞渲染并在初始渲染时需要。

默认情况下,图像的获取优先级较低。一旦浏览器确定此图像需要尽快展示,它可以将获取优先级更改为高优先级:

图像默认的优先级是"低",在"layout"发生之后,浏览器将会被看见的图片的优先级更新为"高",在此期间,高优先级的图片会更先被下载。

Fetch Priority API 能让我们告诉浏览器我们对一个资源的需求优先级。比如说,如果我们确定某个图片是 LCP 图片,我们就可以给他设置高优先级。

html 复制代码
<img src="LCPimage.jpg" fetchpriority="high" alt="Important image!">

从另一个角度来考虑,如果我们知道某个资源并不重要,我们给这个资源设置低优先级,以便其他重要的资源可以先下载。

别瞎用这个功能!如果所有资源都重要,那么就没资源重要了,所以很多时候直接用浏览器的默认配置就行。

优先级较低的 fetch 带来的影响

在大多数场景下,一开始不使用 fetch prioritization,保守估计,LCP 会延迟 0.3s 左右出现。

另一方面,你可能会给很多资源设置"fetchpriority="high",过多的设置并不是一个好的使用方式,因为这样可能会导致fetchpriority不生效,拖累了FCP和LCP。

相关推荐
gqkmiss14 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃19 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰24 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye30 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm32 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
J老熊42 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
猿java1 小时前
什么是 Hystrix?它的工作原理是什么?
java·微服务·面试
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript