script 标签中 defer 和 async 的区别
script标签
当浏览器加载HTML并遇到script标签时,它无法继续构建DOM。它必须立即执行脚本。 外部脚本也是如此:浏览器必须等待脚本下载,然后执行下载的脚本,然后才能处理页面的其余部分。
如果页面顶部又一个很大的脚本,那个这个脚本会阻塞页面,在下载并运行之前,页面是什么都没有的
html
<p>页面可见</p>
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- 下面的内容在脚本加载完之前是不可见的 -->
<p>页面不可见</p>正常做法是将脚本放到body底部进行加载
defer
当script标签设置了defer属性后,浏览器不再等待脚本的加载,会继续处理HTML,构建DOM,脚本在后台继续加载。 当DOM完全构建完后再执行(DOMContentLoaded事件之前执行)。
html
<p>页面可见</p>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- 不等待脚本,立即显示 -->
<p>页面可见</p>defer脚本保持相对顺序来执行,就像常规脚本一样 例如:有两个延迟脚本:long.js 和 small.js:
html
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>这两个脚本会并行下载,small.js 可能会比long.js先下载完成,但是执行的时候依然会先执行 long.js
所以defer用于对脚本执行顺序有严格要求的情况
async
当script标签设置了async属性后,脚本是完全独立的
- 浏览器不会阻止async脚本
- 其他脚本也不会等待async脚本,async脚本也不会等待其他脚本
- DOMContentLoaded和async脚本不会互相等待
- DOMContentLoaded可能在async脚本执行之前触发(如果async脚本在页面解析完成后完成加载)
- 或在async脚本执行之后触发(如果async脚本很快加载完成或在 HTTP 缓存中)
简单来说就是 async 脚本在后台加载完就立即运行
html
<p>1111</p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM 完全加载以及解析"));
</script>
<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>
<p>2222</p>
- 页面内容立即显示:async不阻塞
- DOMContentLoaded可能发生在async之前或之后
- small.js先加载完就会在long.js之前执行,但如果long.js在之前有缓存,那么long.js先执行
Dynamic scripts
还可以使用js创建script,然后将脚本添加到页面
javascript
const script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script);当脚本被添加到文档后立即开始加载, 默认情况下,动态脚本表现为"async"
可以设置 script.async=false,这样脚本会表现为 defer
总结
script 是会阻碍 HTML 解析的,只有下载好并执行完脚本才会继续解析 HTML defer 和 async有一个共同点:下载此类脚本都不会阻止页面呈现(异步加载),区别在于:
- async 执行与文档顺序无关,先加载哪个就先执行哪个;defer会按照文档中的顺序执行
- async 脚本加载完成后立即执行,可以在DOM尚未完全下载完成就加载和执行;而defer脚本需要等到文档所有元素解析完成之后才执行
