<script> 的 async 与 defer 有什么不同?
2023年2月10日
「HTML 的 <script>
当中, <script async>
与 <script defer>
有什么差别?」这一题可以分成两个阶段来答,先讨论为什么需要async
与defer
这两个属性,接着再分析两者的差别。以下为这题的拟答笔记。
为什么需要 async
与 defer
?
之所以会有async
与defer
的出现,是因为浏览器在解析 HTML 时,如果遇到<script>
标签,就会先停下手边建构 DOM 的工作,开始载入<script >
的脚本资源,并执行下载好的脚本。直到下载与执行完毕后,才会继续 DOM 的建构。
这将会造成两难的局面,如果把 <script>
标签顺序摆比较上面,则可能导致如果脚本比较肥大,要载入比较久,进而导致画面比较晚才被渲染出来。这是为什么在过去许多人会把<script>
标签放在最下面,然而,如果这么做,则会导致当渲染完画面后,还要等script
的载入与执行,这会让画面虽然渲染出来,但是没办法有功能。
这是为什么后来 HTML 在 <script>
标签当中加入了 async
与 defer
。这两个属性都是在跟浏览器说,不用等脚本的载入,继续建构 DOM。这让 DOM 的建构与脚本的载入能够同步进行,让使用者体验可以更好。
async
与 defer
有什么不同?
虽然两者有类似的功能,但 async
与 defer
仍有所不同,也适合用在不同的情境。
defer
defer
会告诉浏览器,不用等脚本的下载与执行,可以继续完成 HTML 的解析与 DOM 的建构;在建构 DOM 的同时,会在背景中载入脚本,因此 defer 不会挡住画面的渲染。如果脚本在 HTML 解析完成前就下载好,会等到 HTML 都完全解析后,才会执行。因此如果有脚本是需要等 HTML 解析完、DOM 完整建立后才能载入,那么会需要选 defer
。
如果同时有多个带有 defer
属性的 <script>
资源,浏览器会同步下载,只是会依照在 HTML 中的顺序执行,这可以确保执行时是依照我们想要的顺序。有些时候,可能某个脚本会依赖另一个脚本,例如 A 依赖 B,这时候要确保 A 是在 B 之前执行,这时会选择用 defer
。
async
async
的意思是 asynchrnous,意即非同步,换句话说解析 HTML 与载入脚本,是非同步进行。因此,同样会告诉浏览器,在解析 HTML 时不用等 <script>
脚本的下载与执行。但跟 defer
不同的地方在于, async
的脚本载入与 HTML 解析是彼此独立的,因此只要下载完就会马上执行,而不是像 defer
会等到 HTML 解析完成才执行。
除了跟 DOM 的建构是彼此独立之外,带有 async
属性的脚本跟其他的脚本也是彼此独立,哪个先下载完成就先执行。虽然在下载时不会暂停 HMTL 的解析,但在执行时脚本时会暂停解析。通常如果脚本载入跟 DOM、其他脚本是没有相互依赖关系,例如 Google Analytics 这类分析用的脚本。