css加载会造成阻塞吗?

终于考试完了,今天突然想起来前阵子找实习的时候,今日头条面试官问我,js 执行会阻塞 DOM 树的解析和渲染,那么 css 加载会阻塞 DOM 树的解析和渲染吗?所以,接下来我就来对 css 加载对 DOM 树的解析和渲染做一个测试。

为了完成本次测试,先来科普一下,如何利用 chrome 来设置下载速度

  1. 打开 chrome 控制台 (按下 F12), 可以看到下图,重点在我画红圈的地方
  2. 点击我画红圈的地方 (No throttling), 会看到下图, 我们选择 GPRS 这个选项
  3. 这样,我们对资源的下载速度上限就会被限制成 20kb/s,好,那接下来就进入我们的正题

css 加载会阻塞 DOM 树的解析渲染吗?

用代码说话:

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

假设: css 加载会阻塞 DOM 树解析和渲染

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

实际结果: 如下图

css 会阻塞 DOM 树解析?

由上图我们可以看到,当 css 还没加载完成的时候,h1 并没有显示,但是此时控制台输出如下

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

css 加载会阻塞 DOM 树渲染?

由上图,我们也可以看到,当 css 还没加载出来的时候,页面显示白屏,直到 css 加载完成之后,红色字体才显示出来,也就是说,下面的内容虽然解析了,但是并没有被渲染出来。所以,css 加载会阻塞 DOM 树渲染。

个人对这种机制的评价

其实我觉得,这可能也是浏览器的一种优化机制。因为你加载css的时候,可能会修改下面DOM节点的样式,如果css加载不阻塞DOM树渲染的话,那么当css加载完之后,DOM树可能又得重新重绘或者回流了,这就造成了一些没有必要的损耗。所以我干脆就先把DOM树的结构先解析完,把可以做的工作做完,然后等你css加载完之后,在根据最终的样式来渲染DOM树,这种做法性能方面确实会比较好一点。

css 加载会阻塞 js 运行吗?

​ 由上面的推论,我们可以得出,css 加载不会阻塞 DOM 树解析,但是会阻塞 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 加载语句后面的代码迟迟没有执行,直到 css 加载完成后,它才执行。这也就说明了,css 加载会阻塞后面的 js 语句的执行。详细结果看下图 (css 加载用了 5600+ms):

结论

由上所述,我们可以得出以下结论:

  1. css 加载不会阻塞 DOM 树的解析
  2. css 加载会阻塞 DOM 树的渲染
  3. css 加载会阻塞后面 js 语句的执行、

因此,为了避免让用户看到长时间的白屏时间,我们应该尽可能的提高 css 加载速度,比如可以使用以下几种方法:

  1. 使用 CDN(因为 CDN 会根据你的网络状况,替你挑选最近的一个具有缓存内容的节点为你提供资源,因此可以减少加载时间)
  2. 对 css 进行压缩 (可以用很多打包工具,比如 webpack,gulp 等,也可以通过开启 gzip 压缩)
  3. 合理的使用缓存 (设置 cache-control,expires, 以及 E-tag 都是不错的,不过要注意一个问题,就是文件更新后,你要避免缓存而带来的影响。其中一个解决防范是在文件名字后面加一个版本号)
  4. 减少 http 请求数,将多个 css 文件合并,或者是干脆直接写成内联样式 (内联样式的一个缺点就是不能缓存)

以上,就是所有内容。觉得还不错的点个推荐呗 hhhh, 欢迎交流

本文地址在 ->本人博客地址, 欢迎给个 start 或 follow