一、async 和 defer 的作用
一句话概括:async 和 defer 是为了解决脚本阻塞问题提出的两种解决方案。
为什么要脚本阻塞呢?这里的脚本指的是 JS 脚本。HTML 元素是按其在页面中出现的次序调用的,JavaScript 用于管理页面上的 DOM 元素,如果 JavaScript 脚本的加载先于 HTML 的加载,JavaScript 操纵一个不存在的 DOM 元素,必然会出错。
解决此问题的方法是把脚本元素放在文档体的底端(</body>
标签之前,与之相邻),这样脚本就可以在 HTML 解析完毕后加载了。
这样的方案又带来了新的问题:只有在所有 HTML DOM 加载完成后才开始脚本的加载/解析过程。对于有大量 JavaScript 代码的大型网站,可能会带来显著的性能损耗。这也是 async 属性诞生的初衷。
二、async 和 defer 的区别
浏览器遇到 async 脚本时不会阻塞页面渲染,而是直接下载然后运行。这样脚本的运行次序就无法控制,只是脚本不会阻止剩余页面的显示。当页面的脚本之间彼此独立,且不依赖于本页面的其它任何脚本时,async 是最理想的选择。
比如,如果你的页面要加载以下三个脚本:
<script async src="js/vendor/jquery.js"></script>
<script async src="js/script2.js"></script>
<script async src="js/script3.js"></script>
三者的调用顺序是不确定的。jquery.js 可能在 script2 和 script3 之前或之后调用,如果这样,后两个脚本中依赖 jquery 的函数将产生错误,因为脚本运行时 jquery 尚未加载。
解决这一问题可使用 defer 属性,脚本将按照在页面中出现的顺序加载和运行:
<script defer src="js/vendor/jquery.js"></script>
<script defer src="js/script2.js"></script>
<script defer src="js/script3.js"></script>
添加 defer 属性的脚本将按照在页面中出现的顺序加载,因此第二个示例可确保 jquery.js 必定加载于 script2.js 和 script3.js 之前,同时 script2.js 必定加载于 script3.js 之前。
三、脚本调用策略小结
如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async。
如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中。
本文参考链接:什么是 JavaScript?