性能
性能优化方向
HTML 性能考虑
每个网站都以 HTML 文档的请求开始,这个请求在你的网站加载速度中起着很大的作用。这个模块涵盖了重要的概念,如HTML 缓存,解析器阻塞,渲染阻塞,等等,所以你可以确保你的网站的 HTML 的第一个请求是正确的。
最小化重定向
- HTTP 重定向:当客户端请求一个资源时,服务器可能会返回一个重定向响应,告诉客户端资源已经被移动到新的位置。重定向有两种类型:永久重定向(301 Moved Permanently)和临时重定向(302 Found)。永久重定向表示资源已经被永久地移动到新的位置,而临时重定向表示资源只是暂时地移动到新的位置。
- 重定向的影响:重定向会减慢页面加载速度,因为它需要浏览器在新的位置发起额外的 HTTP 请求来获取资源。
- 重定向的类型:有两种类型的重定向:同源重定向和跨源重定向。同源重定向是在你的源(origin)内部发生的,你可以完全控制这种重定向。跨源重定向是由其 他源发起的,你通常无法控制这种重定向。
- 跨源重定向:跨源重定向通常被广告、URL 缩短服务和其他第三方服务使用。虽然你无法控制跨源重定向,但你仍然应该避免多次重定向,例如,一个广告链接到一个 HTTP 页面,然后该页面又重定向到其 HTTPS 等价物,或者一个跨源重定向到达你的源,但然后触发了一个同源重定向。
常见的同一原始重定向模式是将用户从尾声结尾的 URL 重定向到非拖动斜线等效或 vice-versa,例如,将用户从 example.com/page/重定向到 example.com/page。在页面之间创建内部链接时,需要避免链接到以重定向响应的页面,并直接链接到正确的位置。
措施:
- 避免不必要的重定向:首先,你应该检查你的网站是否有不必要的重定向。例如,如果你有一个页面 A,它重定向到页面 B,然后页面 B 又重定向到页面 C,那么你应该直接将页面 A 重定向到页面 C,避免中间的重定向。
- 使用 HTTP/2:HTTP/2 支持服务器推送,这意味着服务器可以在浏览器请求资源之前就将资源发送给浏览器。这可以减少由于重定向而产生的额外 HTTP 请求。
- 预加载资源:你可以使用
<link rel="preload">
标签来预加载重定向后的资源。这样,当浏览器遇到重定向时,它已经预加载了重定向后的资源,从而减少了加载时间。 - 使用 HSTS:如果你的网站支持 HTTPS,你可以使用 HTTP Strict Transport Security (HSTS) 来避免 HTTP 到 HTTPS 的重定向。HSTS 告诉浏览器只使用 HTTPS 来访问你的网站,从而避免了重定向。
- 优化广告和第三方服务:如果你的网站使用了广告或第三方服务,你应该确保它们不会产生不必要的重定向。你可以与这些服务的提供商联系,要求他们优化他们的重定向。
缓存 HTML 响应
缓存HTML响应很困难,因为响应可能包括指向其他关键资源(例如CSS,JavaScript,图像和其他资源类型)的链接。这些资源可能在其各自的文件名中包含独特的指纹,该指纹根据文件的内容更改。这意味着的缓存的 HTML 文档可能会在部署后变成陈旧,因为它将包含对过时的子资源的引用。
尽管如此,较短的缓存生命周期比不缓存更有好处,比如允许在CDN上缓存资源,减少来自源服务器和浏览器的请求数量,允许重新验证资源,而不是再次下载资源。此方法最适合在任何上下文中都不会更改的静态内容,并且可以将缓存资源的适当时间设置为认为合适的分钟数。5 分钟的静态 HTML 资源是一个安全的赌注,并确保定期更新不会被忽视。
如果页面的 HTML 内容以某种方式个性化(例如对身份验证的用户)进行个性化,那么很可能根本不想缓解(安全性和新鲜度)。如果用户的浏览器缓存 HTML 响应,则无法使缓存无效。因此,最好避免在这种情况下完全缓存 HTML。
缓存 HTML 的一种谨慎的方法是使用 ETag 或 Last-Modified 响应头。ETag 也被称为实体标记头,它是唯一表示所请求资源的标识符,通常使用资源内容的散列
每当资源发生变化时,必须生成一个新的 ETag 值。在后续请求中,浏览器通过 If-None-Match 请求头发送 ETag 值。如果服务 器上的 ETag 与浏览器发送的 ETag 匹配,则服务器响应 304 Not Modified 响应,浏览器使用缓存中的资源。虽然这仍然会导致网络延迟,但 304 Not Modified 响应比整个 HTML 资源要小得多。
但是,重新验证资源的新鲜度所涉及的网络延迟仍然是它自己的缺点。与 web 开发的许多其他方面一样,权衡和妥协是不可避免的。以这种方式缓存 HTML 的额外努力是否值得,或者最好是保持安全,根本不需要缓存 HTML 内容,这取决于。
测量服务器响应时间
如果未缓存响应,则服务器的响应时间高度依赖于托管服务提供商和后端应用程序堆栈。提供动态生成响应(例如从数据库获取数据)的网页很可能比静态网页具有更高的 TTFB,静态网页可以立即提供,而无需在后端花费大量计算时间。显示加载微调器,然后在客户端获取所有数据,将工作从更可预测的服务器端环境转移到可能不可预测的客户端环境。最小化客户端工作量通常会导致改进以用户为中心的指标。
如果用户在现场遇到缓慢的 TTFB,可以通过使用 Server-Timing
响应标头来公开有关在服务器上花费时间的信息:
Server-Timing: auth;dur=55.5, db;dur=220
Server-Timing
标头的值可以包含多个指标,以及每个指标的持续时间。然后,可以使用导航计时 API 从现场用户那里收集这些数据,并进行分析以查看用户是否遇到延迟。在前面的代码片段中,响应标头包括两个计时:
- 对用户进行身份验证的时间 (
auth
),耗时 55.5 毫秒。 - 数据库访问时间 (
db
),耗时 220 毫秒。
可以在优化 TTFB 指南中找到有关
Server-Timing
响应标头的更多信息。https://web.dev/articles/optimize-ttfb#understanding_high_ttfb_with_server_timing
可能还想查看的托管基础设施,并确认有足够的资源来处理的网站收到的流量。共享主机提供商通常容易受到高 TTFB 的影响,而提供更快响应时间的专用解决方案可能成本更高。
可以在 ismyhostfastyet.com 比较热门托管服务提供商的 TTFB。这些数据由从 Chrome 用户体验报告 (CrUX) 数据集中收集的真实用户体验组成。
压缩
基于文本的响应(如 HTML、JavaScript、CSS 和 SVG 图像)应进行压缩,以减少其在网络上的传输大小,以便更快地下载。使用最广泛的压缩算法是 gzip 和 Brotli。Brotli 比 gzip 提高了大约 15% 到 20%。
压缩通常由大多数网络托管服务提供商自动设置,但如果能够自己配置或调整压缩设置,则需要考虑一些重要事项:
- 尽可能使用 Brotli。如前所述,Brotli 比 gzip 提供了相当明显的改进,并且所有主流浏览器都支持 Brotli。尽可能使用 Brotli,但如果的网站在旧版浏览器上被大量用户使用,请确保使用 gzip 作为后备,因为任何压缩都比完全没有压缩好。
- 文件大小很重要。非常小的资源(小于 1 KiB)不能很好地压缩,有时甚至根本不压缩。任何类型的数据压缩的有效性都取决于压缩算法可以处理的大量数据,以便找到更多可压缩的数据位。文件越大,压缩效果越好,但是,不要仅仅因为可以更有效地压缩资源而提供非常大的资源。大型资源(例如 JavaScript 和 CSS)在浏览器解压缩后需要花费更多时间来解析和评估它们,并且即使它们只是略有变化,也可能会更频繁地更改,因为任何更改都会导致不同的文件哈希
- 了解动态压缩与静态压缩。动态压缩和静态压缩是何时应压缩资源的不同方法。动态压缩在请求资源时压缩资源,有时在每次请求资源时都会压缩资源。另一方面,静态压缩会提前压缩文件,不需要在请求时执行压缩。静态压缩消除了压缩本身所涉及的延迟,在动态压缩的情况下,延迟可能会增加服务器响应时间。静态资源(如 JavaScript、CSS 和 SVG 图像)应静态压缩,而 HTML 资源(尤其是为经过身份验证的用户动态生成的资源)应动态压缩。
- 自行进行压缩具有挑战性, 通常最好让内容分发网络 (CDN)(将在下一节中讨论)为处理此问题。但是,了解这些概念可以帮助辨别托管服务提供商是否正确使用压缩。这些知识可以帮助找到改进压缩设置的机会,以便它们为的网站带来最大的好处。
内容分发网络
内容分发网络 (CDN) 是一种分布式服务器网络,用于缓存源服务器中的资源,然后从物理上更接近用户的边缘服务器为资源提供服务。与用户的物理距离减少了往返时间 (RTT),而 HTTP/2 或 HTTP/3、缓存和压缩等优化使 CDN 能够比从源服务器获取内容更快地提供内容。在某些情况下,使用 CDN 可以显着改善网站的 TTFB。
关键路径
本模块涵盖了浏览器如何渲染网页背后的一些理论,特别是完成页面初始渲染所需的内容
关键渲染路径是 web 性能中的一个概念,用于处理页面在浏览器中初始呈现的速度。本模块将介绍关键渲染路径背后的理论,涵盖诸如
- 渲染阻塞
- 解析器阻塞
资源等概念,以及它们如何在页面在浏览器中显示的速度中发挥关键作用。
关键路径
关键呈现路径是指在网页开始在浏览器中呈现之前所涉及的步骤。要呈现页面,浏览器需要 HTML 文档本身以及呈现该文档所需的所有关键资源。
渐进式渲染
web 本质上是分布式的。与使用前安装的本机应用程序不同,浏览器不能依赖于具有呈现页面所需的所有资源的网站。因此,浏览器非常擅长逐步呈现页面。本机应用通常有一个安装阶段,然后是一个运行阶段。但是,对于网页和 Web 应用程序,这两个阶段之间的界限要少得多,并且浏览器在设计时专门考虑到了这一点。
一旦浏览器拥有渲染页面的资源,它通常会开始这样做
- 何时渲染?
- 什么时候为时过早?
如果浏览器在只有一些 HTML 时尽快渲染,但在它没有任何 CSS 或必要的 JavaScript 之前,那么页面将暂时看起来破碎,并在最终渲染中发生很大变化。这比最初在一段时间内呈现空白屏幕更糟糕,直到浏览器拥有提供更好用户体验的初始呈现所需的更多资源。
另一方面,如果浏览器等待所有资源可用,而不是进行任何顺序渲染,那么用户将等待很长时间;
如果页面在更早的时间点可用,则通常不必要
浏览器需要知道它应该等待的最小资源数,以避免呈现明显损坏的体验。另一方面,在向用户显示某些内容之前,浏览器也不应等待超过必要的时间。浏览器在执行初始渲染之前执行的一系列步骤称为关键渲染路径。
了解关键渲染路径有助于提高 Web 性能,确保不会阻止初始页面呈现超过必要的范围。与此同时,通过从关键渲染路径中删除初始渲染的必要资源,也不要让渲染过早发生,这一点也很重要。
步骤
关键渲染路径的步骤如下:
- 从 HTML 构造文档对象模型 (DOM)
- 从 CSS 构造 CSS 对象模型 (CSSOM)
- 应用任何更改 DOM 或 CSSOM 的 JavaScript
- 从 DOM 和 CSSOM 构造渲染树
- 在页面上执行样式和布局操作,以查看哪些元素适合何处
- 绘制内存中元素的像素
- 如果像素中的任何一个重叠,则合成像素
- 将所有生成的像素物理绘制到屏幕上
用图表示
只有在完成所有这些步骤后,用户才会在屏幕上看到内容。
这些步骤更多详情,移步:https://developer.chrome.com/articles/renderingng-architecture
此渲染过程会发生多次;
初始呈现会调用此过程,但随着更多影响页面呈现的资源可用,浏览器将重新运行此过程(或者可能只是其中的一部分)以更新用户看到的内容;
关键渲染路径侧重于先前为初始渲染概述的过程,并依赖于该过程所需的关键资源。
关键资源
那有哪些关键资源呢?
浏览器需要等待下载一些关键资源,然后才能完成初始渲染。这些资源包括:
- HTML 的一部分
<head>
元素中的 CSS(阻塞渲染)<head>
元素中的 JavaScript(阻塞渲染 )
一个关键点是浏览器以流方式处理 HTML。一旦浏览器获取页面 HTML 的任何部分,浏览器就会开始处理它;
然后,浏览器可以(并且经常这样做)决定在接收页面的其余部分之前很好地呈现它;
重要的是,对于初始渲染,浏览器通常不会等待:
- 所有 HTML
- 字体
- 图像
<head>
元素之外的非渲染阻塞 JavaScript(例如, 放置在 HTML 末尾的<script>
元素)<head>
元素外部的非渲染阻塞 CSS,或属性media
值不适用于当前视口的 CSS
字体和图像通常被浏览器视为在后续页面重新呈现期间要填充的内容,因此它们不需要保留初始呈现;
但是,这可能意味着在初始渲染中会留下空白区域,而文本则隐藏在等待字体上,或者直到图像可用;
更糟糕的是,当没有为某些类型的内容预留足够的空间时(尤其是当 HTML 中没有提供图像尺寸时),页面的布局可能会在稍后加载此内容时发生变化;
用户体验的这一方面通过累积布局偏移 (CLS) 指标来衡量;
<head>
元素是处理关键渲染路径的关键;
优化 <head>
元素的内容是 Web 性能的一个关键方面。但是,要了解关键呈现路径,只需要知道 <head>
元素包含有关页面及其资源的元数据,但用户看不到任何实际内容;
可见内容包含在 <head>
元素后面的 <body>
元素中。在浏览器可以 呈现任何内容之前,它既需要要呈现的内容,也需要有关如何呈现它的元数据;
但是,并非 <head>
元素中引用的所有资源对于初始页面呈现都是绝对必需的,因此浏览器只会等待那些资源;
若要确定关键呈现路径中包含哪些资源,需要了解渲染阻塞和解析器阻塞。
渲染阻塞
某些资源被认为非常重要,以至于浏览器会暂停页面呈现,直到处理完它们。默认情况下,CSS 属于此类别;
当浏览器看到 CSS 时(无论是 <style>
元素中的内联 CSS,还是 <link rel=stylesheet href="...">
元素指定的外部引用资源),浏览器都会避免呈现更多内容,直到它完成该 CSS 的下载和处理;
注意:虽然 CSS 默认情况下是渲染阻塞的,但可以通过更改
<link>
元素的media
属性来指定与当前条件不匹配的值,将其转换为非渲染阻塞资源:<link rel=stylesheet href="..." media=print>
。这在过去曾被用于允许非关键 CSS 以非渲染阻塞方式加载;
仅仅因为资源阻止渲染并不一定意味着它会阻止浏览器执行任何其他操作。浏览器试图尽可能高效,所以当浏览器看到它需要下载一个 CSS 资源时,它会请求它并暂停渲染,但仍然会继续处理其余的 HTML,并寻找其他工作;
渲染阻塞资源(如 CSS),被发现时,会阻塞页面的所有渲染,这意味着某些 CSS 是否被渲染阻塞取决于浏览器是否发现了它;
某些浏览器(最初是 Firefox,现在还有 Chrome)仅阻止渲染阻塞资源下方的内容渲染,这意味着,对于关键的渲染阻塞路径,我们通常对 <head>
中的渲染阻塞资源感兴趣,因为它们有效地阻塞了整个页面的渲染;
最近的一项创新是 blocking=render
添加到 Chrome 105 中的属性。这允许开发人员在处理元素之前将 <link>
或 <script>
<style>
元素显式标记为渲染阻塞,但仍允许解析器在此期间继续处理文档。
解析器阻塞
解析器阻塞资源是那些阻止浏览器通过继续解析 HTML 来查找其他工作的资源;
默认情况下,JavaScript 是解析器阻塞的(除非明确标记为异步或延迟),因为 JavaScript 可以在执行时更改 DOM 或 CSSOM。因此,浏览器不可能继续处理其他资源,直到它知道所请求的 JavaScript 对页面的 HTML 的全部影响。因此,同步 JavaScript 会阻止解析器;
解析器阻塞资源实际上也是渲染阻塞,由于解析器在完全处理之前无法继续通过解析阻塞资源,因此它无法访问和渲染其之后的内容;
浏览器可以在等待期间渲染到目前为止收到的任何 HTML,但在关键渲染路径方面,任何解析器阻塞资源 <head>
实际上意味着所有页面内容都被阻止渲染;
阻塞解析器可能会产生巨大的性能成本,而不仅仅是阻塞渲染,出于这个原因,浏览器将尝试通过使用称为预加载扫描程序的辅助 HTML 解析器来降低此成本,以便在主 HTML 解析器被阻止时下载即将到来的资源;
虽然不如实际解析 HTML 好,但它至少允许浏览器中的网络功能在被阻止的解析器之前工作,这意味着它将来不太可能再次被阻止。
识别阻塞资源
许多性能审计工具可识别渲染和解析器阻塞资源。WebPageTest 在资源的 URL 左侧用橙色圆圈标记呈现阻塞资源:
在开始渲染之前,需要下载并处理所有渲染阻塞资源,瀑布流中的深绿色实线表示;
Lighthouse 还突出显示了阻塞渲染资源,但方式更微妙,并且仅当资源实际延迟页面渲染时。这有助于避免误报,可以最大程度地减少渲染阻塞。通过 Lighthouse 运行与前面的 WebPageTest 图相同的页面 URL 只会将其中一个样式表标识为渲染阻塞资源;
优化关键渲染路径
优化关键渲染路径涉及:
- 减少接收 HTML 的时间(由上一个模块中详述的首字节时间 (TTFB) 指标表示)
- 减少渲染阻塞资源的影响
很长一段时间以来,关键渲 染路径一直关注初始渲染。然而,已经出现了更多以用户为中心的 Web 性能指标,这让人质疑关键渲染路径的终点应该是第一次绘制,还是随后的更令人满意的绘制之一。
另一种观点是将时间集中在最大内容绘制 (LCP) 之前,甚至是第一个内容绘制 (FCP) - 作为内容渲染路径(或其他人可能称之为关键路径)的一部分。在这种情况下,可能需要包含不一定阻塞的资源(这是关键渲染路径的典型定义),但对于渲染内容绘制是必需的。
无论对“关键”的确切定义如何,了解是什么阻碍了任何初始渲染和关键内容都很重要;
第一种绘制测量为用户渲染任何内容的第一个可能机会。理想情况下,这应该是有意义的东西,而不是像背景颜色这样的东西,但即使它是非内容性的,向用户呈现一些东西仍然有价值,这是衡量传统定义的关键渲染路径的论据。同时,衡量主要内容何时呈现给用户也有价值;
识别关键渲染路径
许多工具可以识别 LCP 元素及其渲染时间。除了 LCP 元素之外,Lighthouse 还将帮助识别 LCP 阶段以及每个阶段所花费的时间,以帮助了解优化工作的最佳重点:
对于更复杂的站点,Lighthouse 还在单独的审核中突 出显示了关键请求链:
此 Lighthouse 审核会观察以高优先级加载的所有资源,因此它包括 Chrome 设置为高优先级资源的 Web 字体和其他内容,即使它实际上并不阻塞渲染。
优化资源加载
当页面加载时,它的 HTML 中引用了许多资源,这些资源通过 CSS 提供了页面的外观和布局,并通过 JavaScript 提供了其交互性。在本模块中,将介绍与这些资源相关的一些重要概念,以及它们如何影响页面的加载时间。
渲染阻塞
CSS 是一种渲染阻塞资源,因为它会阻止浏览器渲染任何内容,直到构建 CSS 对象模型 (CSSOM);
浏览器会阻止渲染以防止出现无样式内容闪现 (FOUC),从用户体验的角度来看,这是不可取的。
可以在其中看到没有任何样式的页面。随后,一旦页面的 CSS 完成从网络的加载,就会应用所有样式,并且页面的无样式版本立即替换为样式版本;
一般来说,FOUC 是通常看不到的东西,但理解这个概念很重要,这样就可以知道为什么浏览器会阻止页面渲染,直到 CSS 下载并应用到页面为止;
渲染阻塞不一定是不可取的,但可以通过保持 CSS 优化来最大程度地缩短渲染阻塞的持续时间;
解析器阻塞
解析器阻塞资源会中断 HTML 解析器,例如没有 async
或 defer
属性的 <script>
元素;
当解析器遇到 <script>
元素时,浏览器需要先解析并执行脚本,然后再继续解析 HTML 的其余部分。这是设计使然,因为脚本可能会在 DOM 仍在构建期间修改或访问 DOM;
当使用外部 JavaScript 文件(没有 async
或 defer
)时,从发现文件到下载、解析和执行该文件,解析器都会被阻止。当使用内联 JavaScript 时,解析器同样会被阻塞,直到内联脚本被解析并执行为止。
解析器阻塞
<script>
还必须等待任何正在运行的渲染阻塞 CSS 资源到达并被解析,然后浏览器才能执行它。这也是设计使然,因为脚本可以访问渲染阻塞样式表中声明的样式(例如,通过使用element.getComputedStyle()
)
预加载扫描器
预加载扫描器是一种辅助 HTML 解析器形式的浏览器优化,它会扫描原始 HTML 响应,以便在主 HTML 解析器发现资源之前查找并推测性获取资源。例如,预加载扫描器将允许浏览器开始下载 <img>
元素中指定的资 源,即使 HTML 解析器在获取和处理 CSS 和 JavaScript 等资源时被阻止也是如此。
要利用预加载扫描器,关键资源应包含在服务器发送的 HTML 标记中。预加载扫描程序无法发现以下资源加载模式:
- CSS 使用
background-image
属性加载图像。这些图像引用位于 CSS 中,预加载扫描程序无法发现; - 使用 JavaScript 注入 DOM 中的
<script>
元素标记形式的动态加载脚本或使用动态import()
加载的模块; - 使用 JavaScript 在客户端呈现 HTML。此类标记包含在 JavaScript 资源的字符串中,预加载扫描器无法发现;
- CSS
@import
声明;
这些资源加载模式都是后期发现的资源,因此不会从预加载扫描程序中受益。尽可能避免它们。但是,如果无法避免此类模式,可以使用 preload
提示来避免资源发现延迟;
CSS 决定页面的呈现和布局。如前所述,CSS 是一种渲染阻塞资源,因此优化 CSS 可能会对整个页面加载时间产生相当大的影响;
CSS
CSS 决定页面的呈现和布局。如前所述,CSS 是一种渲染阻塞资源,因此优化 CSS 可能会对整个页面加载时间产生相当大的影响。
压缩
缩小 CSS 文件可以减小 CSS 资源的文件大小,从而加快下载速度。这主要是通过从源 CSS 文件中删除内容(例如空格和其他不可见字符)并将结果输出到新优化的文件来完成的:
/* 未压缩: */
/* Heading 1 */
h1 {
font-size: 2em;
color: #000000;
}
/* Heading 2 */
h2 {
font-size: 1.5em;
color: #000000;
}
/* 压缩后 */
h1,
h2 {
color: #000;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
一些高级 CSS 缩小器可能会采用额外的优化,例如将冗余规则合并到多个选择器中。然而,这种高级 CSS 优化可能存在风险,并且可能无法在所有 CSS 方法或设计系统中正常工作或扩展。
从最基本的形式来看,CSS 缩小是一种有效的优化,可以提高网站的 FCP,在某些情况下甚至可以提高 LCP
bundlers等工具可以在生产版本中自动为执行此优化。
删除不使用的 css
在渲染任何内容之前,浏览器需要下载并解析所有样式表;
完成解析所需的时间还包括当前页面未使用的样式;
如果使用的bundlers将所有 CSS 资源组合到一个文件中,用户可能会下载比渲染当前页面所需的更多 CSS;
要发现当前页面未使用的 CSS,请使用 Chrome DevTools 中的 Coverage 工具;
Chrome DevTools 中的Coverage工具对于检测当前页面未使用的 CSS(和 JavaScript)非常有用。它可用于将 CSS 文件拆分为多个资源以由不同页面加载,而不是传送可能延迟页面渲染的更大的 CSS 包。
避免使用 @import
虽然看起来很方便,但应该避免在 CSS 中使用 @import
声明:
/* Don't do this: */
@import url("style.css");
与 HTML 中 <link>
元素的 工作方式类似,CSS 中的 @import
声明允许从样式表中导入外部 CSS 资源;
这两种方法之间的主要区别在于 HTML <link>
元素是 HTML 响应的一部分,因此比通过 @import
声明下载的 CSS 文件发现得更快;
为了发现 @import
声明,必须首先下载包含它的 CSS 文件;
这会产生所谓的请求链,就 CSS 而言,它会延迟页面初始渲染所需的时间。另一个缺点是使用 @import
声明加载的样式表无法被预加载扫描器发现,因此成为后期发现的渲染阻塞资源;
在大多数情况下,可以使用 <link rel="stylesheet">
元素替换 @import
。 <link>
元素允许同时下载样式表并减少总体加载时间,这与连续下载样式表的 @import
声明相反。
注意:如果需要使用
@import
(例如级联层或第三方样式表),可以通过对导入的样式表使用preload
指令来减轻延迟。此外,CSS 预处理器(例如 SASS 或 LESS)通常使用@import
语法作为开发人员体验改进的一部分,允许单独且更加模块化的源文件。但是,当 CSS 预处理器遇到@import
声明时,引用的文件将被捆绑并写入单个样式表,从而避免@import
在纯 CSS 中导致的连续请求损失。
内联关键 CSS
下载 CSS 文件所需的时间会增加页面的 FCP。在文档 <head>
中内联关键样式可以消除对 CSS 资源的网络请求,并且如果操作正确,可以在用户的浏览器缓存未准备就绪时缩短初始加载时间;
剩余的 CSS 可以异步加载,或者附加在 <body>
元素的末尾。
关键词:关键 CSS 是指渲染初始视口中可见内容所需的样式。最初的视口概念有时被称为“首屏”。页面上的其余内容保持无样式,而其余 CSS 则异步加载。
重要提示:提取和维护关键样式可能很困难。
- 应该包含哪些样式?
- 应定位哪个视口?
- 该过程可以自动化吗?
- 如果用户在非关键 CSS 加载之前向下滚动,会发生什么情况?
- 如果用户遇到 FOUC,他们会受到什么影响?
这些都是应该考虑的好问题,因为网站的架构可能会使使用关键 CSS 变得异常困难;
然而,在某些特定情况下,性能优势可能值得付出努力,因此请调查关键 CSS 是否是网站的可行选择!
缺点是,内联大量 CSS 会向初始 HTML 响应添加更多字节;
由于 HTML 资源通常无法缓存很长时间(或根本无法缓存),这意味着对于可能在外部样式表中使用相同 CSS 的后续页面,不会缓存内联 CSS;
测试并衡量页面的性能,以确保所做的权衡是值得的。
javaScript
JavaScript 驱动了网络上的大部分交互性,但它是有代价的;
传送过多的 JavaScript 可能会使的网页在页面加载期间响应缓慢,甚至可能导致响应速度问题,从而减慢交互速度,这两种情况都会让用户感到沮丧。
当加载不带 defer
或 async
属性的 <script>
元素时,浏览器会阻止解析和渲染,直到下载、解析并执行脚本;
类似地,内联脚本会阻塞解析器,直到脚本被解析并执行。
async 和 defer
async
和 defer
允许加载外部脚本而不阻塞 HTML 解析器,而带有 type="module"
的脚本(包括内联脚本)会自动延迟。但是, async
和 defer
有一些需要理解的差异;
使用 async
加载的脚本在下载后立即解析并执行,而使用 defer
加载的脚本在 HTML 文档解析完成时执行 - 这与浏览器的 DOMContentLoaded
事件;
此外, async
脚本可能会无序执行,而 defer
脚本则按照它们在标记中出现的顺序执行;
默认情况下,使用 type="module"
属性加载的脚本会被延迟,而通过使用 JavaScript 将 <script>
标记注入 DOM 来加载的脚本的行为类似于 async
脚本;
客户端渲染
一般来说,应该避免使用 JavaScript 来呈现任何关键内容或页面的 LCP 元素。这称为客户端渲染,是单页应用程序 (SPA) 中广泛使用的一种技术;
JavaScript 呈现的标记会绕过预加载扫描器,因为客户端呈现的标记中包含的资源无法被它发现。这可能会延迟关键资源的下载,例如 LCP 图像;
浏览器仅在脚本执行后开始下载 LCP 图像,并将元素添加到 DOM。反过来,脚本只有在被发现、下载和解析后才能执行。这被称为关键请求链,应该避免;
此外,与响应导航请求而从服务器下载的标记相比,使用 JavaScript 呈现标记更有可能生成较长的任务;
广泛使用 HTML 的客户端呈现会对交互延迟产生负面影响。在页面 DOM 非常大的情况下尤其如此,当 JavaScript 修改 DOM 时,这会触发大量的渲染工作。