Athon

关键请求 | CSS-Tricks

原文链接: css-tricks.com

网站服务看起来很简单:发送HTML,浏览器进行处理并加载资源,然后我们只要耐心的等页面渲染好即可。

但是你很少知道,整个过程背后发生了“很多”事情。

你有没有想过,浏览器“如何”知道应该加载哪些资源,并且以什么顺序来加载这些资源?

今天我们来看看如何利用资源优先级来提高加载速度。

页面资源的加载流程

大多数浏览器使用流解析器来解析HTML,资源未被完全加载完之前,会被标记,它们会被添加到一个网络队列中,并带有一个预先确定的优先级。

在Chrome中,资源有以下几种优先级:很低[Very Low],低[Low] ,中[Medium],高[High]和非常高[Very high]。在Chrome DevTools的资料中,有一些不同的叫法: 最低[Lowest],低[Low], 中[Medium], 高[High]和最高[Highest].

如果你想看你网站的请求顺序,你可以通过Chrome DevTools中网络请求标签来查看各请求的优先级。

如果你使用Safari,它同样包含网络请求列表(新的!)这个功能。

通过右键单击任何请求表标题来筛选请求顺序列表。

你还能在性能标签[Performance tab]中找到请求的顺序。

鼠标移入时显示资源的时间和优先级。

Chrome如何定义资源的优先级?

每种资源类型(CSS、JavaScript、字体等)都有自己的一套规则,规定它们将如何被排序。下面是关于网络优先级计划的非详尽清单:

HTML— 最高优先级

Styles—最高优先级。 使用‘@import’引用的样式同样也是最高优先级,但是会排在阻塞脚本之后。

Images是唯一可以根据视口动态改变优先级的资源。所有图像以低优先级开始,但在可见视口中渲染时将被提升为中等优先级。视口之外的图像(也称为“折叠下方”)依旧保持低优先级。

在写这篇文章的时候,我发现(在 Paul Irish的帮助下)Chrome DevTools将图片的低优先级误报为中优先级,Paul写过一个错误报告,你可以在这里进行查看

如果你想要看下Chrome是如何处理图片的优先级的,你可以从更新所有图片优先级计算资源优先级这两篇文章开始。

Ajax/XHR/fetch()—高优先级High priority.

Scripts 遵循一个复杂的优先级加载方案。(Jake Archibald在2013年详细描述了这个方案如果你想研究它背后的科学,我建议你可以深入的研究下)。TL;DR 版本是这样描述的:

  • 如果script被标记在图片之前,且其未使用特殊属性,它会被标记为高优先级。

  • 如果script被标记在图片之后,且其未使用特殊属性,它会被标记为中优先级。

  • 如果script使用‘async’或'defer'属性,它会被标记为低优先级。

  • 如果script使用es6的type="module`"`属性,它会被标记为低优先级。

Fonts 有点奇怪;它们是非常重要的资源(谁会喜欢“我看到它了!”,“现在他不见了”,“哇,一个新字体!”这种游戏呢?)所以字体会被以最高优先级来下载。

不幸的是,很多@font-face标签都包含在外链的css中(未使用特殊的加载方式)。这意味着字体往往会在样式加载完之后才进行加载。

假设的CSS文件通过 @font-face引用了字体,当你的字体选择器映射到页面上的节点时,该字体才会被请求。假设你构建了一个单页面应用程序,当文本并未渲染的时候,对应的字体也会一直等待加载。

什么是关键请求?

大多数网站让浏览器加载渲染页面的所有资源,并没有“隐藏元素”的具体概念。

早在之前,浏览器不会在同一域名下同时请求超过6个请求—人们通过使用assets-1.domain.tld, assets-2.domain.tld域名来尝试绕过这种限制,同时下载更多的资源,但是并未认识到这会导致每个新域名和静态资源都会需要DNS解析和TCP链接。

虽然这种方法有一些优点,但我们中的许多人并没有完全理解它的影响,当然也没有高质量的浏览器开发工具来验证它们。

谢天谢地,现在我们有了很棒的工具。以CNN为例,我们来确认下当可视区域渲染时(也有人把它理解为“有用的区域”),静态资源是否被完全加载

用户更关注的内容是标题和主要内容。

展示该屏信息时,只有5个部分是必要的(并不是所有这些都需要在站点可用之前加载。)

  • 最重要的,HTML。假设其他请求都失败了,用户依旧可以阅读该页

  • CSS

  • 标题(一张用CSS放置的PNG背景图片。这可能是内嵌的SVG)

  • “4(!)” web字体

  • 文章头图

这些静态资源(注意不包括Javascript)是构成页面主视窗可视区域所必需的。它们是应该首要被加载的

但是我们通过Chrome的性能查看标签看到,在字体和图片加载之前,大概有50个请求

在不太稳定的4G环境下记录,CNN.com大概在9s的时候才被完全渲染。

我们查看页面所需的理想请求,和显示的请求有着明显的不匹配。

控制资源加载优先级

现在我们已经定义了什么是关键请求,我们可以开始使用几个简单而强大的调整来对它们进行优先级排序。

预加载让浏览器把font.woff放在加载队列的高优先级中。

注意:font.woff应该备以高优先级下载的原因是as="font"—它是一个字体,所以它遵循我们在之前“Chrome如何确立资源的优先级”章节所谈到的优先级规则。

就好像你在告诉浏览器,“你可能还不知道它,但是我们将要使用它。”

这对我们之前定义的关键请求来说是“完美”的。Web字体几乎可以被分类为"绝对关键",但是针对字体如何被发现并下载则有一些根本性问题:

  • 我们等待CSS被加载、解析和应用,然后才能解析到@font-face规则。

  • 除非通过选择器将CSS规则匹配到DOM,否则不会将该字体添加到浏览器的请求队列中。

  • 选择器匹配在样式冲计算的过程中发生。他不一定“立刻”在被样式下载之后立刻发生,当主线程繁忙的时候,它可能被拖延。

在大多数情况下,字体被延迟显示了几秒钟,仅仅因为我们没有指示浏览器及时下载它们。

在一个拥有低端CPU,不稳定链接的手机上,假设没有适当的回退方案,会导致很严重的问题。

预加载实践:字体

我在calibreapp.com上跑了两个测试。在 第一个测试中,我并未对网站进行任何修改。在第二个里面,我加了这两个标签:

<link rel="preload" as="font" href="" type="font/woff2" crossorigin />

<link rel="preload" as="font" href="" type="font/woff2" crossorigin />

下面,你将看到这两个测试的呈现的可视化比较。结果是相当惊人的:

当字体被预加载时,页面渲染速度加快了3.5秒

图片下面:字体被预加载 — 这个网站在3G连接下5秒就渲染完成了。

也接受media=""属性”,它将根据“@media”查询规则选择性地对资源进行优先排序:

`<link rel="preload" href="article-lead-sm.jpg" as="image" type="image/jpeg" media="only screen and (max-width: 48rem)">`

在这里,我们可以为小屏幕设备预加载特定的图像。对于“超棒的主图”来说是完美的。

正如上面展示的,我们使用简单的几个标签,很大的改善了页面的请求和渲染性能,超级大。

在web字体上更进一步

69%网站使用web字体, 不幸的是,他们在大多数情况下体验并不太好。它们出现,然后消失,然后再次出现,改变权重并在渲染序列中改变页面。

坦率地说,这几乎在每一个层面上都很糟糕。

正如您在上面看到的,控制字体的请求顺序和优先级对渲染速度有很大的影响。显然,我们应该在大多数情况下优先考虑Web字体请求

我们可以使用CSSfont-display属性进一步改进。允许我们控制在请求和加载Web字体过程中字体显示的方式。

显示方式有四种选项,但是我建议使用font-display: swap;,在Web字体加载完之前,使用后备字体进行显示--会直接替换该文字。

像这样定义一系列字体:

body {
  font-family: Calibre, Helvetica, Arial;
}

浏览器会在Calibre字体加载完成之前,先使用Helvetica字体(或者Arial字体,如果你没有先使用Helvetica字体字体的话)进行展示。现在只有Chrome和Opera支持font-display,但这是跨越性的异步,从今天起就没有理由不使用它了。

保持页面性能

正如你所知道的,网站从来都不是“完整的”。总是会有改进,它会让人感觉到势不可挡,很快。

Calibre是一个自动化的工具,用于审核性能、易访问性和Web最佳实践,它将帮助你保持优势。

正如您在上面看到的,有几个指标是理解用户性能的关键。

  • 第一个渲染,告诉我们什么时候浏览器开始“从无内容到有内容”

  • 第一个有意义的渲染,告诉我们什么时候浏览器进行“有用的渲染”

  • 最后,第一个交互 将告诉你什么时候页面被完全渲染,并且JavaScript主线程已经落定(CPU已经平稳了几秒钟)。

在这里,我们为CNN的“第一个有意义的渲染”制定一个预期。

您可以针对所有这些关键用户体验指标设置预期。当超出预期(或满足预期)时,你的团队将通过Slack、电子邮件或你喜欢的任何地方得到通知。

Calibre 显示网络请求优先级,这样你就可以确定所做的请求。调整优先级并改进性能。

我希望你已经学到了一些宝贵的技能来检查请求和他们的优先事项,并激发了一些想法来进行实验,以进行有意义的性能改进。

你的关键请求检查清单

  • ✅ 启用Chrome DevTools请求优先列表

  • ✅ 决定哪个请求必须在用户可以看到整个渲染页面前完成。

  • ✅ 尽可能减少所需的关键请求的数量。

  • ✅ 很多使用的静态资源“很可能”被使用在网站的下一页。

  • ✅ 使用 nopush HTTP请求头来告诉浏览器我们需要在HTML完全下载完之前预请求哪些资源。

  • ? HTTP/2 Server push是棘手的,请避免使用它。 (看看Tom Bergan, Simon Pelchat 和 Michael Buettner写的informative document还有Jake Archibald的 "HTTP/2 Push is tougher than I thought"

  • ✅ 在可能的情况下对Web 字体使用font-display: swap;

  • ⏱ Web字体整在被使用吗?他们可以被移除吗?如果不:优先考虑他们并且使用WOFF2!

  • ⏱ 延迟加载脚本会延迟你的单页应用程序显示任何东西吗?

  • ? 看看这个free screencast 还有 Front End Center ,告诉我们如何使用几乎最好的后备经验来加载字体。

  • ? 看下 chrome://net-internals/#events 并且加载一个页面—它会显示网络相关的事件。

  • 没有请求比没有请求更快[No request is faster than no request]. ✌️