HTML5给
<script>
增加了两个布尔属性,defer
和async
,两者在使用上有点像又有点区别,很容易混淆。本文尝试理清之间的关系。
defer
defer script
不会阻塞其他脚本的下载和HTML解析,但它会等到其他DOM构建完成才执行,然后才会触发DOMContentLoaded
事件。也就是说,如果defer script
下载得快,需要等一会(DOM构建完成)才执行。defer script
有点像放在body最后面的script,但下载不受页面阻塞。
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<head>
<script src="/scripts/defer_script.js" defer></script>
</head>
<body>
<!-- 10000项,HTML解析比较慢 -->
{% for item in arr %}
<p>Number {{ item }}</p>
{% endfor %}
</body>
当defer script
放在<head>
里,HTML解析比较慢时,defer script
下载完之后没有立即执行,而是等到document最后几个节点解析时才执行。 defer script
会阻塞后面的defer script
的执行,但不会阻塞后面的async script
和普通script的执行。
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<head>
<script src="/scripts/defer_script1.js?t=50" defer></script>
<script src="/scripts/defer_script2.js" defer></script>
</head>
<body>
{% for item in arr %}
<p>Number {{ item }}</p>
{% endfor %}
</body>
defer_script1
在defer_script2
之前,但是下载比较慢,最终依然是defer_script1
先执行。 把defer_script2
的defer去掉或者改成async,都是defer_script2
先执行。
async
async script
的下载同样不会阻塞其他脚本的下载和HTML解析,但是执行会阻塞HTML解析。 想象这样的场景,async script
下载很快,HTML解析速度一般,async script
执行时间很长。可以看到,async script
的执行阻塞了HTML解析。 async script
执行顺序不受前面的defer script
或者async script
影响,但会被前面的普通script的执行阻塞。
1
2
3
4
5
6
7
<!DOCTYPE html>
<head>
<script src="/scripts/async_script2.js?t=50" async></script>
<script src="/scripts/async_script1.js" async></script>
</head>
<body>
</body>
async_script2
在document中位置比async_script1
靠前,但由于网络请求会延时约50ms,最终执行也比async_script1
晚。
结论
- 相同点
- 只用于外联script,
<script>
没有src属性时忽略该defer、async属性; - script下载与HTML解析并行。
- 只用于外联script,
- 不同点
defer script
的执行在HTML解析完成之后,而async script
的执行会阻塞HTML解析;- 多个
defer script
的执行顺序与它们在document出现的顺序一致,async script
的执行顺序依赖于它们下载完成的快慢。 - 同时使用
defer
、async
,async
的优先级更高。
应用场景
- 像jquery之类的有依赖关系的script,建议使用defer,保证jquery执行完才执行其他三方脚本;
- 像数据埋点之类相对独立的script,建议使用async,但是要注意如果脚本下载得比较快,可能影响页面性能。