现代 CSS

消除阻塞页面渲染的资源

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

在 Web 页面渲染过程中,有些资源文件(比如 HTML、CSS 和 JavaScript)会阻塞页面的渲染。当浏览器遇到渲染阻塞资源时,它就会停止下载其余的资源,直到这些关键文件得到处理(关键资源)。在此期间,整个渲染过程被搁置。另一方面,非渲染阻塞资源并不会延迟页面的渲染。浏览器可以在最初的页面渲染之后在后台安全地下载它们。

然则,并不是所有被浏览器视为阻塞渲染的资源都是首屏渲染所必需的;这完全取决于页面的特征。你可以使用一些技术手段(最佳实践),将这些非关键的渲染阻塞资源变成非渲染的阻塞资源。此外,你也可以减少那些仍然关键的,无法消除的阻塞渲染的资源数量或大小。了解阻塞渲染资源将使你能够解决常见的 Web 性能问题。

什么是关键的渲染路径

浏览器通过关键的渲染路径(CRP)将 HTML、CSS 和 JavaScript 这些文件转换为用户所看到的页面。会经历:

  • 下载HTML
  • 读取 HTML,并同时
    • 构建 DOM 树
    • 遇到 <link rel="stylesheet"> 标签,并下载 CSS
  • 读取 CSS,并构建 CSSOM
  • 将 DOM 和 CSSOM 合并为一个渲染树
  • 使用渲染树,计算布局(每个元素的大小和位置)
  • 绘制,或将像素渲染到页面

这个过程越短,关键渲染时间就越短,页面首屏幕渲染就越短。浏览器在完成这些步骤之前,用户将看到一个空白的白色页面。

什么是阻塞渲染的资源

阻碍渲染的资源是指在 关键渲染路径(CRP)上被“按下暂停”的文件(该文件也被称为关键资源)。它们打断了一个或多个步骤(关键渲染路径中的步骤)。

从技术上,HTML 是阻断渲染的,因为你需要它来构建 DOM 树。如果没有 HTML,我们甚至不会一个可渲染的页面。然而,HTML通常不是我们问题的根源:

  • CSS 是阻塞渲染的:浏览器在创建 CSSOM 之前需要它,而 CSSOM 会阻止所有的后续步骤。一旦浏览器遇到 CSS 样式表<link rel="stylesheet"><style> 标签,它就必须下载并解析其内容。然后,它必须在继续进行其余的渲染之前创建 CSSOM。在 CSSOM 和 DOM 都被创建之前,渲染树不能继续。
  • JavaScript 可能会阻塞渲染:当浏览器遇到一个要同步运行的 JavaScript 脚本时,它会停止 DOM 的创建,直到该脚本运行完毕。即 同步的 JavaScript(没有asyncdefer)在下载和执行 JavaScript 的过程中都会阻塞 HTML 解析器。

此外,如果 CSS 出现在 JavaScript 脚本之前,那么在 CSSOM 被创建之前,JavaScript 将不会被执行。这是因为 JavaScript 有可能操作 DOM 或 CSSOM。

注意,图片和字体并不阻塞页面的渲染。它们可能是由于渲染被阻塞或其他性能问题而导致渲染缓慢。

阻塞渲染资源:CSS

CSS 加载不会阻塞 DOM 树的解析

由浏览器渲染引擎解析 Web 页面的过程中可以获知:DOM解析和CSS解析是两个并行的进程,因此 CSS 加载不会阻塞 DOM 树的解析!

CSS 加载会阻塞 DOM 树的渲染

渲染树是依赖于 DOM 树和 CSSOM 树的,所以无论 DOM 树是否已经完成,它都必须等到 CSSOM 构建完成,即 CSS 加载完成(或失败)后,才能开始渲染。因此, CSS 加载是会阻塞 DOM 树的渲染。

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            var p = document.querySelector('p')
            console.log(p)
        })
    </script>
    <link rel="stylesheet" href="./static/style.css?sleep=3000">
</head>

<body>
    <p>hello world</p>
</body>

CSS 的加载并没有 阻塞 DOM 树的解析,<p> 标签是正常解析的,但 <p> 标签加载完后,页面是迟迟没有渲染的,是因为 CSS 还没有请求完成,在 CSS 请求完成后,“hello world” 文本才被渲染出来,所以 CSS 会阻塞页面渲染。

CSS 加载会阻塞其后的 JS 执行

JS 脚本的加载、解析与执行会阻塞 DOM 的构建,也就是说,在构建 DOM 树时,HTML 解析器若遇到了 JS 脚本,那么HTML解析器会暂停 DOM 的构建,将控制权移交给 JS 引擎,等 JS 引擎运行完毕后,浏览器再从中断的地方恢复 HTML 的解析,继续构建 DOM树。

<html>
    <head>    
        <link href="theme.css" rel="stylesheet">
    </head>
    <body>    
        <div>hello world</div>    
        <script>        
            console.log('hello world')    
        </script>    
        <div>hello world</div>
    </body>
</html>

它的执行流程:

此时 CSS 也阻塞 DOM 树的生成

这是因为 JS 脚本不只是可以改变 DOM,它还可以更改 CSSOM(更改样式)。而不完整的 CSSOM 是无法使用的, JS 中想访问 CSSOM 并是更改它,那么在执行 JS 时,必须要能拿到完整的 CSSOM。

这就会导致一个现象,如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此运行 JS 脚本,那么浏览器将延迟 JS 脚本执行和 DOM 构建,直至其完成 CSSOM 的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建 CSSOM,然后执行 JS 脚本,最后在继续构建 DOM。

阻塞渲染资源: JavaScript

默认情况下,JS 脚本会阻止 DOM、其后 CSSOM的构建,也就延迟了页面首次渲染

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            var p = document.querySelector('p')
            console.log(p)
        })
    </script>
    <link rel="stylesheet" href="./static/style.css?sleep=3000">
    <script src="./static/index.js"></script>
</head>

<body>
    <p>hello world</p>
</body>

HTML 文件中引用了外部资源 CSS 和 JS 文件,HTML会同时发起这两个文件的下载请求,不管 CSS 文件还是 JS 文件谁先到达,都要先等到 CSS 文件下载完成并生成 CSSOM,然后再执行 JS 脚本,最后再继续构建 DOM,构建布局树,绘制页面。

不论是内联还是外链 JS 都会阻塞后续 DOM 解析,DOMContentLoaded 事件会被延迟,后续的 DOM 渲染也会被阻塞。这意味着,JS 脚本的执行过程中插入的元素会先于后续的 HTML 展现,即使JS 脚本是外链资源也是如此。由于 JS 脚本只会阻塞后续的 DOM,前面的 DOM 在解析完成后会被立即渲染给用户。

为什么阻塞渲染资源对性能影响很大

阻塞渲染资源会使绘制速度变慢,导致最大内容的绘制(LCP)变慢,也会致使首屏渲染变慢。

据相关研究描述,一个网站如果加载速度的时间超过 2s ,就会以指数级的速度失去访问者。例如,加载时间为 2s 的网站有 6% 的跳失率。这意味着每 100 人访问你的网站,就会失去其中 6 人(超过 2s 让用户会感到不耐烦,不愿意花时间等)。如果网站加载时间在 3s 左右,跳失率会攀升至 11% 。而到了 5s 的时候,跳出率达到了 38% 。这意味着有三分之的用户因为网站加载慢而流失。因此,消除阻塞渲染资源,提高网站性能就变得非常的重要。但消除阻塞渲染资源只是为了提高网站性能的一部分,还有其他很多事情需要做。然而,消除阻塞渲染资源是一个比较容易的,通过一些小的调整就可以迅速解决。

如何测试网站是否存在阻塞渲染的资源

我们的网站上都有阻塞渲染的资源。如果这些阻塞渲染资源过多时就会严重影响 Web 性能。当这种情况发生时,可以使用一些工具来帮我检测出来,比如 LighthousePageSpeed Insights等工具。

当使用 Lighthouse 测试一个页面时,如果页面存有 阻塞渲染资源 存在,那么在检测报告的“机会(Opportunities)”选项中会列出所有阻塞渲染资源的 URL:

Lighthouse 会标记出两种阻塞渲染资源的类型,即 CSS 文件 和 JS 脚本文件的 URL。

<script> 标签:

  • 放在 </head> 标签中的 <script> 标签
  • 没有设置 asyncdefermodule 属性的 <script> 标签

<link rel="stylesheet"> 标签:

  • 没有设置 disabled 属性(当这个属性存在时,浏览器不会下载样式表)
  • 不匹配的媒体查询

聚划算首页为例,其 <head> 中有六个 <script> 标签:

  • 三个引入外部 JS 的 <script> ,并且其中一个设置了 async 、一个设置了 defer 属性,另一个既没有设置 asyncdefer 属性,也没有设置 module 属性
  • 三个内联的 JS 的 <script>
  • 三个内联的 CSS 的 <style>
  • 没有外联 CSS和 <link>

如果使用 WebPageTest 测试的话,它将在一个真实的移动设备上运行性能测试。一旦测试完成,点击测试结果中的瀑布图。每个阻塞渲染资源都会有一个橙色关闭按钮图标的标记:

如何消除阻塞渲染的资源

我们的目标不是要消除所有的阻塞渲染的资源,而是要降低它们对性能的影响。接下来我们分别从 CSS 和 JS 两部分看如何消除阻塞渲染的资源。

如何消除阻塞渲染的 CSS

浏览器渲染引擎在渲染一个 Web 页面时, DOM的解析成本并不高(DOM操作成本非常昂贵),但 CSSOM 的解析是很昂贵的。而 CSS 本身就是 阻塞渲染的一种资源,在能够请求,接收和处理所有 CSS 样式之前,浏览器不会开始渲染任何页面内容,即阻塞了页面的渲染。如果我们希望 CSS 不被成为一种阻塞页面渲染的资源,就

剩余80%内容付费后可查看
返回顶部