# 资源加载顺序

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>

我们假设 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>

假设: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 绘制,合成图层,显示在屏幕

Rendering

从图解可以看出

  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的执行。

# DOMContentLoaded

对于浏览器来说,页面加载主要有两个事件,一个是 DOMContentLoaded,另一个是 onLoad

onLoad

等待页面的所有资源都加载完成才会触发,这些资源包括css、js、图片视频等

DOMContentLoaded

当页面的内容解析完成后,则触发该事件

正如我们上面讨论过的,CSS会阻塞DOM渲染和JS执行,而JS会阻塞DOM解析。那么我们可以做出这样的假设

  1. 当页面只存在css,或者js都在css前面,那么DomContentLoaded不需要等到css加载完毕。
  2. 当页面里同时存在css和js,并且js在css后面的时候,DomContentLoaded必须等到css和js都加载完毕才触发。

# CSS不会阻塞 DOM Ready

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>css阻塞</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script>
      document.addEventListener('DOMContentLoaded', function() {
        console.log('DOMContentLoaded');
      })
    </script>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
  </head>
  <body>
  </body>
</html>

可以看到,CSS还未加载完,就已经触发了 DOMContentLoaded事件了。因为CSS后面没有任何JS代码。

# CSS会阻塞 DOM Ready

<html lang="en">
  <head>
    <title>css阻塞</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script>
      document.addEventListener('DOMContentLoaded', function() {
        console.log('DOMContentLoaded');
      })
    </script>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet"><script>
      console.log('到我了没');
    </script>
  </head>
  <body>
  </body>
</html>

可以看到,只有在css加载完成后,才会触发 DOMContentLoaded 事件,因为 CSS 后面有JS代码

# 结论

  1. CSS加载 不会阻塞 DOM 🌲的解析
  2. CSS加载 会阻塞 DOM 🌲 的渲染
  3. CSS加载 会阻塞 后面JS 语句的执行
  4. CSS下面没有JS时 不会阻塞 DOM Ready
  5. CSS下面有JS时 会影响 DOM Ready
更新时间: 11/4/2020, 6:20:57 PM