# 资源加载顺序

CSS 和 JS 是否会阻塞页面解析渲染?

# CSS 加载会阻塞 DOM 树解析和渲染吗

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      h1 {
        color: red !important;
      }
    </style>
    <link
      href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css"
      rel="stylesheet"
    />
    <script>
      function h() {
        console.log(document.querySelectorAll("h1"));
      }
      setTimeout(h, 0);
    </script>
  </head>

  <body>
    <h1>这是红色的</h1>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

我们假设 CSS 会阻塞 DOM 树解析和渲染,那么会出现以下情况:

在 bootstrap.css 还没加载完之前,下面的内容不会被解析渲染。那么我们一开始看到的应该是白屏,h1 不会显示出来。并且此时 console.log 的结果应该是一个空数组。

# CSS 会阻塞 DOM 树解析?

打开浏览器,我们发现在 bootstrap.css 还没加载完成的时候,h1 并没有显示,但是此时控制台输出 [h1],可以得知:

DOM 树至少已经解析完成到了 h1 那里,而此时 css 还没加载完成,也就说明,CSS 并不会阻塞 DOM 树的解析。

# CSS 加载会阻塞 DOM 树渲染?

上面操作可以发现,在 CSS 未加载完之前,页面都是处于白屏,CSS 加载完后,红色字体才显示出来.下面的内容虽然解析了,但是并没有被渲染出来。所以: CSS 加载会阻塞 DOM 树渲染。

# CSS 加载会阻塞 JS 运行吗

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>css阻塞</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script>
      console.log("before css");
      var startDate = new Date();
    </script>
    <link
      href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css"
      rel="stylesheet"
    />
  </head>

  <body>
    <h1>这是红色的</h1>
    <script>
      var endDate = new Date();
      console.log("after css");
      console.log("经过了" + (endDate - startDate) + "ms");
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

假设:CSS 会阻塞后面的 JS 运行,在 link 后面的 js 代码,应该要在 CSS 加载完成后才会输出

经过运行我们发现,位于 CSS 加载前的 JS 先执行,位于 CSS 后面的 JS 等到 CSS 加载完后 才执行,并且经过了 169ms 才执行。我们得出结论: CSS 会阻塞后面 JS 的执行

# 原理解析

浏览器的渲染机制一般分为以下几个步骤:

  1. 处理 HTML 并构建 DOM 树 🌲
  2. 处理 CSS 构建 CSSOM 树 🌲
  3. 将 DOM 和 CSSOM 合并成一个渲染树 🌲
  4. 根据渲染树来布局,计算每个节点的位置
  5. 调用 GPU 绘制,合成图层,显示在屏幕

从图解可以看出

  1. DOM 解析和 CSS 解析是两个并行的进程,它们分别由不同的解析引擎去分析,这也解释了为什么 CSS 加载不会阻塞 DOM 的解析。
  2. 由于 Render Tree 是依赖于 DOM Tree 和 CSSOM Tree 的,所以他必须等待到 CSSOM Tree 构建完成,也就是 CSS 资源加载完成(或者 CSS 资源加载失败)后,才能开始渲染。因此,CSS 加载是会阻塞 Dom 的渲染的
  3. 由于 js 可能会操作之前的 Dom 节点和 css 样式,因此浏览器会维持 html 中 css 和 js 的顺序。因此,样式表会在后面的 js 执行前先加载执行完毕。所以 css 会阻塞后面 js 的执行。

# 结论

  1. CSS 加载 不会阻塞 DOM 🌲 的解析
  2. CSS 加载 会阻塞 DOM 🌲 的渲染
  3. CSS 加载 会阻塞 后面 JS 语句的执行
  4. CSS 下面没有 JS 时 不会阻塞 DOM Ready
  5. CSS 下面有 JS 时 会影响 DOM Ready