解释css和js是如何阻塞浏览器渲染DOM的,并提出一些优化的方案

浏览器是解析DOM生成DOM Tree,结合CSS生成的CSS Tree,最终组成render tree,再渲染页面。

1.CSS不会堵塞DOM解析(DOM Tree的生成)

<!DOCTYPE html>
   <html lang="en">
   <head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="/css/sleep3000-common.css">
  <script defer src="/js/logDiv.js">
   </head>
   <body>
  <div></div>
   </body>
</html>

#1.sleep3000-common.css 会延迟3秒钟返回,样式就是设置div块为浅蓝色
#2.logDiv.js 代码如下,script标签加defer的含义是该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行
const div = document.querySelector('div');
console.log(div);
#3.执行结果
先打印出div这个DOM节点,过3s左右之后才渲染出一个浅蓝色的div。
这就证明了CSS 是不会阻塞 DOM 的解析的,尽管CSS下载需要3s,但这个过程中,浏览器不会傻等着CSS下载完,而是会解析DOM的。          

2.CSS会堵塞页面的渲染

<!DOCTYPE html>
   <html lang="en">
   <head>
  <meta charset="UTF-8">
  <title>Title</title>
       <style>
  div {
  width: 100px;
  height: 100px;
  background: lightgreen;
  }
  </style>
  <link rel="stylesheet" href="/css/sleep3000-common.css">
   </head>
   <body>
  <div></div>
   </body>
</html>

#1.sleep3000-common.css 会延迟3ms返回,样式就是设置div块为浅蓝色
#2.最终结果是浏览器会转圈圈三秒,之后呈现出一个浅蓝色的div,所以结果是CSS会堵塞页面的渲染。

3.JS会堵塞DOM解析

<!DOCTYPE html>
   <html lang="en">
   <head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="/js/blok.js">
   </head>
   <body>
  <div></div>
   </body>
</html>

#1.block.js代码如下
const arr = [];
for (let i = 0; i < 10000000; i++) {
 arr.push(i);
 arr.splice(i % 3, i % 7, i % 5);
}
const div = document.querySelector('div');
#2.最终结果浏览器转圈圈一会,这过程中不会有任何东西出现,之后打印出null。说明JS堵塞了DOM解析。
#3.如果JS文件体积太大,同时你确定没必要阻塞DOM解析的话,不妨按需要加上defer或者async属性,此时脚本下载的过程中是不会阻塞DOM解析的。

4.浏览器遇到 <script>且没有deferasync属性的标签时,会触发页面渲染。如果<script>标签前面有CSS资源,会等到CSS资源加载完毕之后再执行<script>脚本

<!DOCTYPE html>
<html lang="en">
   <head>
       <meta charset="UTF-8">
       <title>Title</title>
       <link rel="stylesheet" href="/css/sleep3000-common.css">
    </head>

     <body>
           <div></div>
           <script src="/js/logDiv.js">
           <style>
               div {
                   background: lightgrey;
              }
           </style>
           <script src="/js/sleep5000-logDiv.js"></script>
           <link rel="stylesheet" href="/css/common.css">
           </body>
</html>

#答案是等待3秒浅蓝色打印div,等待5秒浅灰色打印div,最后浅蓝色。由此可见,每次碰到<script>标签时,浏览器都会渲染一次页面。这是基于同样的理由,浏览器不知道脚本的内容,因而碰到脚本时,只好先渲染页面,确保脚本能获取到最新的DOM元素信息,尽管脚本可能不需要这些信息。

5.优化方案:

<script>最好放底部,<link>最好放头部,如果头部同时有<script><link>的情况下,最好将<script>放在<link>上面

6.附:defer 与 async

如果我们不想<script>堵塞页面的解析,我们可以给<script>标签添加defer或者async属性。defer和async的区别主要如下:

1.执行时机不同:
defer的脚本将在DOM解析完成后、触发 DOMContentLoaded 事件前执行
async的脚本一旦加载完毕,就会执行(不论是在DOM解析阶段还是DOMContentLoaded之前之后,但是一定是在window.load之前执行)
2.执行顺序不同:
defer能保证多个脚本按照书写顺序执行
async不能保证多个脚本的执行顺序

 

posted @ 2020-02-17 21:47  格格123456  阅读(611)  评论(0编辑  收藏  举报