Web 性能优化:控制关键请求的优先级
翻译自:https://calibreapp.com/blog/critical-request,作者 Ben Schwarz,已获得作者授权,原文略作中文语境调整。
性能优化有很多角度,其中一个关键是控制关键请求的优先级,从而达到性能优化的效果。本文以一种相对系统的方式来进行该方法的探讨。
构建一个网站服务看似简单:发送 HTML,浏览器识别出接下来需要加载什么资源。然后,我们耐心的等待页面就绪。
你不知道的是,这背后发生了很多事情。你有没有想过,浏览器是如何判断哪些资产需要以什么顺序被请求的?
内容概览:
什么是资产优先级? Chrome 如何安排资源优先级? 什么样的请求是关键的? Lighthouse 审计:避免关键请求的依赖链。 技术:控制请求优先级。 技术:图片懒加载。 技术:font-display 关键请求检查清单。
什么是资产优先级?
现代浏览器用流式解析器来解析 HTML —— 在完全下载之前,就可以在 HTML 标记之中找到资产。当浏览器找到资产时,就会按照预先确定的优先级把它们加到网络队列中。
可视化的分析一个网站以及它的资源优先级。
预先确定的优先级可能是:Lowest, Low, Medium, High 和 Highest 其中之一。通过优先级分配,浏览器可以知道哪个请求对页面快速加载最关键。
本文以 Chrome 为主,但其他浏览器对请求的优先级排序也是类似的。你可以在 Chrome、Safari、Firefox 或 Edge 开发者工具中,通过右键单击任何表格标题并选择“优先级”来查看请求优先级。
请求优先级也可以在Chrome的 Performance 面板中看到:
Chrome 如何安排资源优先级?
资源按出现的顺序被添加到网络队列中。然后浏览器会把网络活动投入用于尽快获取到最高优先级的资源。
每种资源类型都有自己的一组规则来决定分配给它的优先级:
资源类型 | 优先级 |
---|---|
HTML | Highest |
Fonts | High |
Stylesheets | Highest |
通过 @import 加载的 Stylesheets | Highest,会被安排在阻塞脚本之后。 |
Images | 默认是 Low,在初始视口中渲染时升级为 Medium。 |
JavaScripts | Low, Medium 或 High。查看 Addy Osmani 的 JavaScript Loading Priorities in Chrome[1] 来获取更多细节。 |
Ajax,XHR,或者 fetch() API | High |
什么样的请求是关键的?
关键请求是指显示在页面初始视口里的资源。
这些资源对 Core Web Vitals[2] 中诸如 Largest Contentful Paint(最大内容绘制)[3] 和 First Contentful Paint[4](首次内容绘制)这些指标有着直接的影响。以这篇文章为例,我们可以直观地识别出视口需要的资产,以便完全渲染。
对于这个页面来说,关键的请求是:
HTML CSS LOGO 3 个字权重 头条图片(Largest Contentful Paint 元素)
这些资源(注意,没有 JavaScript)对于初始视口的视觉展示至关重要。他们应该先被加载。
在审计你的页面时,我们推荐:
对页面进行可视化审计,注意视口中的关键元素。 最初的 5 个 HTTP 请求应该是:HTML + 4 个关键请求。 确保关键的请求不要被重定向。 更新你的站点,确保关键资源是优化过、压缩过、有缓存能力且有正确的 HTTP 头的。
Lighthouse 审计:避免关键请求的依赖链
谷歌的 Lighthouse 套件附带了一个名为“避免关键请求的依赖链”的审计,高亮展示了被链接在一起的请求。当浏览器在发出请求是由于另一个请求引用它时(也称为依赖项),我们称之为请求链。
关键请求链最常见的一个例子是,比如某些样式表内部加载了在初始页面视口显示的字体或背景图像。
@font-face {
font-family: 'Calibre';
font-weight: 400;
font-display: swap;
src: url('/Calibre-Regular.woff2') format('woff2'), url('/Calibre-Regular.woff')
format('woff');
}
.carousel-bg {
background-image: url('/images/main-masthead-bg.png');
}
你可以使用 Lighthouse 的“避免关键请求的依赖链”的审计来诊断和识别出页面中的资源依赖。
减少关键请求链的整体数量,有助于更快的 Largest Contentful Paint(最大内容绘制) 和即时的用户体验。
为了减少关键请求链的影响,请使用以下 Web 性能策略:
减少请求的数量 使用压缩和最小化来减少资源的大小 将非关键脚本标记为异步 考虑将 @font-face
声明直接内联到 HTML 中避免使用 CSS 背景图片或 @import
使用预加载提前获取关键资源 使用 bundlephobia[5] 寻找该库的更小替代品
技术:控制请求优先级
请求优先级会被 preload[6] 的使用所影响。预加载的资源会被分配为高优先级,并且在页面的初始加载中优先被请求。
<link rel="preload" href="Calibre-Regular.woff2" as="font" crossorigin />
使用 preload,相当于告诉浏览器:“你可能还不知道它是什么,但我们需要它。”
预加载有助于优化关键请求,但也不要滥用!如果预加载了过多的资源,页面性能会劣化[7]。
预加载会影响 Largest Contentful Paint(最大内容绘制) 和 Cumulative Layout Shift[8](累积布局偏移)。在某些情况下,影响可能是负面的。我们建议使用预加载请求进行试验,但一定要前后仔细测试。
技术:图片懒加载
默认情况下,浏览器会加载 HTML中指定的所有图片,哪怕用户实际上永远不会看见的那些。懒加载允许你指定某些图片只有当用户滚动页面到它们附近时才去请求。如果用户不滚动页面,浏览器就不会加载这些图片。
使用这种方法,您可以提高整体渲染速度,并节省不必要的数据传输。懒加载是提高 Largest Contentful Paint(最大内容绘制)的有效方法。
过去,我们使用第三方库或手写脚本实现懒加载。如今,它已内置在浏览器中[9]。
使用方法如下:
在已经知道会出现在视口下方的 元素上加上
loading="lazy"
属性。当页面滚动时,懒加载的图片已经加载完成,准备好展示了。
累积布局偏移(CLS)标识用户交互期间的页面布局偏移。请确保设置图像的 width 和 height 属性,以避免懒加载的图像在渲染时,页面重新布局。
就是这样!懒加载的实现方法并不复杂,但是对加速渲染非常有帮助。一定要尽可能用上。
技术:font-display
根据 HTTP Archive 中的统计,69% 的站点使用 web 字体[10]。不幸的是,不幸的是,在大多数情况下,它们提供的体验都低于标准。
每个人都目睹过字体出现,然后消失,改变了粗细,页面仿佛被震动了一样。这些移位现在会被累积布局移位(CLS)指标所测量。
我们已经证实了用 控制字体请求的优先级对渲染速度有惊人的影响。所以显而易见,我们在大部分情况下都应该提高字体请求的优先级。
我们可以用 CSS font-display[11] 进一步提高渲染速度。这个 CSS 属性允许你控制字体在请求和加载后如何展示。
你可以用
font-display
来优化 Largest Contentful Paint[12](最大内容绘制)和 Cumulative Layout Shift[13](累积布局偏移)
有五个 font-display 选项[14]供您选择。我们推荐使用 swap
选项,它可以先立即呈现文本,然后在加载网络字体后立即替换。
给定这样的字体栈:
body {
font-family: Calibre, Helvetica, Arial;
}
浏览器会先用 Helvetica 字体展示(或者 Arial,如果你的系统里没有安装 Helvetica)直到 Calibre 字体加载完成。
没有 font-display
文字在 HTML、CSS 和网络字体加载完后才展示:
没有 font-display
,文本在 2.44 秒显示。
加上 font-display: swap
文字在 HTML 下载并且处理后立刻显示了,取得 1.6 秒的进步:
使用 font-display
,文本在 835 毫秒显示。
font-display
在主流现代浏览器里支持的不错[15],你可以从今天开始使用它了。
关键请求检查清单
有了这些知识,您现在应该能够为您的站点选择最关键的资产,并相应地对它们进行优先级排序。如果你想进一步推进优先级和速度的优化,请遵循以下清单:
启用Chrome开发者工具的 Priority 列。 尽可能减少所需的关键请求的数量。 检查哪些请求必须在用户看到完整渲染的页面之前发出。使用 对这些关键请求进行优先处理。
使用 link prefetching[16],优化可能在下一个导航中使用的资源。 使用 Link Preload HTTP headers[17] 声明要在 HTML 完全交付之前预加载的资源。 确保图像尺寸正确的提前写好。 使用内联 SVG 展示 Logo 和图标。 使用更好的图像格式,如 AVIF 或 WEBP。 使用 font-display: swap
在初始渲染中展示文本。使用压缩的字体格式,如 WOFF2 或 variable fonts(可变形字体)。 在 Chrome://net-internals/#events 中查看 Chrome 的网络事件。 记住:最快的请求,是从未发出的请求。
祝你优化快乐!
参考资料
JavaScript Loading Priorities in Chrome: https://tech.bytedance.net/articles/7025506101156118536#:~:text=JavaScript%20Loading%20Priorities%20in%20Chrome
[2]Core Web Vitals: https://tech.bytedance.net/articles/7025506101156118536#:~:text=%E8%BF%99%E4%BA%9B%E8%B5%84%E6%BA%90%E5%AF%B9-,Core%20Web%20Vitals,-%E4%B8%AD%E8%AF%B8%E5%A6%82%20Largest
[3]Largest Contentful Paint(最大内容绘制): https://web.dev/lcp/
[4]First Contentful Paint: https://tech.bytedance.net/articles/7025506101156118536#:~:text=First%20Contentful%20Paint
[5]使用 bundlephobia: https://bundlephobia.com/package/moment@2.29.1
[6]preload: https://tech.bytedance.net/articles/7025506101156118536#:~:text=%E4%BC%98%E5%85%88%E7%BA%A7%E4%BC%9A%E8%A2%AB-,preload,-%E7%9A%84%E4%BD%BF%E7%94%A8%E6%89%80
[7]页面性能会劣化: https://tech.bytedance.net/articles/7025506101156118536#:~:text=%E8%BF%87%E5%A4%9A%E7%9A%84%E8%B5%84%E6%BA%90%EF%BC%8C-,%E9%A1%B5%E9%9D%A2%E6%80%A7%E8%83%BD%E4%BC%9A%E5%8A%A3%E5%8C%96,-%E3%80%82
[8]Cumulative Layout Shift: https://web.dev/cls/
[9]内置在浏览器中: https://caniuse.com/?search=lazy
[10]69% 的站点使用 web 字体: https://httparchive.org/reports/state-of-the-web?start=latest#fonts
[11]CSS font-display: https://css-tricks.com/almanac/properties/f/font-display/
[12]Largest Contentful Paint: https://web.dev/lcp
[13]Cumulative Layout Shift: https://web.dev/cls/
[14]五个 font-display 选项: https://css-tricks.com/almanac/properties/f/font-display/
[15]支持的不错: https://caniuse.com/mdn-api_fontface_display
[16]link prefetching: https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ
[17]Link Preload HTTP headers: https://www.w3.org/TR/preload/
最后
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
欢迎加我微信「qianyu443033099」拉你进技术群,长期交流学习...
关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。