async vs defer 属性

418 阅读1分钟

<script> 元素的 async 和 defer 属性现在浏览器支持度很好,所以是时候了解它们的使用场景和区别了。

图例

<script>

让我们从没有任何属性的 <script> 标签开始。DOM 解析从顶部 HTML 标签开始,直到解析到 <script> 标签,此刻 DOM 解析将停止,并请求获取该脚本文件(如果它是外部的 .js),然后执行完外部脚本后,再继续进行 DOM 解析工作。
这种情况可能因为外部 <script> 脚本文件多或者脚本代码计算量大,从而导致执行脚本时间过长,使的 HTML 页面出现空白现象。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
	<script src="./a.js"></script>
    <script src="./b.js"></script>
	<script>
  	// 脚本执行比较耗时  
  </script>
</head>
<body>
  <div> Text </div>
</body>
</html>

<script async>

在 DOM 解析的时候,异步下载外部脚本文件,当脚本文件下载完成后,将停止 DOM 解析并执行脚本代码。当脚本执行完成后,再继续 DOM 解析工作。

这样的好处是,脚本文件的下载和 DOM 解析工作是并行的,不会阻塞 DOM 的解析工作,从而避免页面空白现象。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
	<script src="./a.js" async></script> 
    <script src="./b.js" async ></script>
	<script>
  	// 脚本执行比较耗时  
  </script>
</head>
<body>
  <div> Text </div>
</body>
</html>

<script defer>

在 DOM 解析期间延迟下载外部脚本文件,当纯 HTML 被完全加载以及DOM解析完成时,DOMContentLoaded 事件会立即触发,然后再执行外部脚本文件代码。

defer 的好处即使外部脚本下载完,也不会立即执行,而是延迟到 DOM 解析之后再立马执行外部脚本,加快 HTML 的解析速度,避免页面空白现象。

defer 也保证按照 <script> 标签出现在文档的顺序执行脚本文件。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
	<script src="./a.js" defer></script> 
    <script src="./b.js" defer ></script>
	<script>
  	// 脚本执行比较耗时  
  </script>
</head>
<body>
  <div> Text </div>
</body>
</html>

以上代码,当DOM 解析完之后,先执行 a.js 脚本文件,再执行 b.js 脚本文件。

什么时候该用什么?

以下是一些规则:

  • 如果每个外部文件不依赖执行的先后顺序,可以使用 async
  • 如果一个脚本依赖另一个脚本的执行,可以使用 defer

浏览器支持

可以通过下面链接分别查看 <script> 标签上 async 和 defer 属性的支持情况。


参考文章:
MDN
async vs defer attributes