js阻塞浏览器ui渲染

js加载与执行阻塞浏览器ui渲染

前言

我们都知道,javascript 脚本应该放置在 html 文档的底部,其实这只是个比较保险的做法,要想javascript 脚本不会阻塞浏览器 ui 的渲染,需要知道以下 javascript 脚本加载与执行对 ui 渲染的影响。

先上结论

下面结论针对外链脚本,在此之前应该明白两个概念,加载 和 执行,加载就是下载到本地,执行就是运行脚本。

  1. 若脚本标签不带 defer 或者 async 属性,则脚本属于同步加载,此时脚本加载会阻塞浏览器解析 html 文档。
  2. 若脚本标签带 defer 或者 async ,则脚本属于异步加载,此时脚本加载不会阻止浏览器解析 html 文档。
  3. 脚本执行都会阻塞浏览器 ui 渲染(dom树构建,css树构建,渲染树构建,重排,重绘)。
  4. 在浏览器中 javascipt 和 ui 渲染公用一个线程,但是下载的时候浏览器会有一个线程池维护一些 io操作(网络IO,磁盘IO),所以下载的时候通过设置可以不阻塞 ui 渲染。

在此说一下, defer 和 async 的相同点在于 都是异步加载脚本,区别在于 async 加载完会立即执行,而 defer 是在 dom 树解析(仅仅是dom结构,不管样式表,图片这些)之后执行。

测试

针对上面的结论,我们挨个测试。

1. 若脚本标签不带 defer 或者 async 属性,则脚本属于同步加载,此时脚本加载会阻塞浏览器解析 html 文档。

看下面代码 html:

<!-- demo.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="http://localhost/study/add.php" ></script>
    <style>
        .box {
            width: 400px;
            height: 400px;
            background: yellow;
        }
    </style>
</head>
<body>
    <div class="box">1ddd2223fff3f1</div>

</body>
</html>

add.php

<?php

sleep(2);

将php 放到网站根目录下,打开 demo.html 即可测试,在此我们看到 add.php 返回的脚本大小很小,但是我们通过 sleep 模拟加载慢的情况,也就是延长加载时间,我们打开浏览器可以看到,经过一段时间后 页面上才渲染出 box 元素。

2. 若脚本标签带 defer 或者 async ,则脚本属于异步加载,此时脚本加载不会阻止浏览器解析 html 文档

看下面代码:

<!-- demo.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="http://localhost/study/add.php"  defer ></script>
    <style>
        .box {
            width: 400px;
            height: 400px;
            background: yellow;
        }
    </style>
</head>
<body>
    <div class="box">1ddd2223fff3f1</div>

</body>
</html>

add.php

<?php

sleep(2);

我们给 script 标签加上 defer 或者 async ,打开浏览器看看,可以看到,box 元素瞬间显示在页面上。

3. 脚本执行都会阻塞浏览器 ui 渲染(dom树构建,css树构建,渲染树构建,重排,重绘)。

看下面代码:html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="demo1.js"></script>
    <style>
        .box {
            width: 400px;
            height: 400px;
            background: yellow;
        }
    </style>
</head>
<body>
    <div class="box">1ddd2223fff3f1</div>

</body>
</html>

demo1.js

let count = 0;
for (let i = 0; i < 1000000000; i ++) {
    count ++;
}

打开浏览器我们发现,浏览器会过一段时间才会显示 box 元素,在这里我们通过大量计算,占用了线程,因为浏览器中 ui渲染和 javascript 公用一个线程,所以会阻塞。

最后

献上一张图(盗的)-.-
js阻塞浏览器ui渲染