script标签的几种下载执行模型

225 阅读2分钟

在 HTML 中会遇到以下三类 script:

  • <script src='xxx'></script>
  • <script src='xxx' async></script>
  • <script src='xxx' defer></script>

前提

  • 如果脚本是内联的,浏览器会先去执行这段内联的脚本,如果是外链的,那么先会去加载脚本,然后执行。
  • async和defer模型不适用于内联脚本

默认的模式

  • 普通JS的下载和解析都会影响DOM解析

image.png

async模式

  • 在执行到加了async的script时会先下载,然后再去执行下一个标签。待到这个script的外链下载完毕时,如果DOM正在渲染则暂停,执行async script的内容。多个async先下载完的先执行

  • async和 DOMContentLoaded 无任何绑定关系。

image.png

  • 也有可能html先解析完毕了,script还没下载完,会出现以下情况

image.png

defer模式

  • 在 defer 模式下,当浏览器解析到带有 defer 属性的<script>标签时,会开始在后台下载脚本[多个 defer 脚本会并行下载],但不会阻塞后续 HTML 的解析。待 HTML 解析完成后,浏览器会等待所有 defer 脚本下载完毕,然后按照它们在 HTML 中出现的顺序依次执行这些脚本,等所有 defer 脚本执行完毕后,才会触发 DOMContentLoaded 事件。

image.png

如果defer的script也放在</body>前一行,则绿色走完才走紫色和红色。此时对DOM渲染和DOMContentLoaded的时机的触发和普通script完全相同。

总结

script 标签JS 执行顺序是否阻塞解析 HTMLDOMContentLoaded回调
<script>依次等待
<script async>先下载完先执行DOM未解析完时阻塞不等待
<script defer>先下载完所有defer再依次执行等待

动态插入的script

  • 动态插入的 JavaScript(js)默认是异步加载模式。
  • 常见的实现方式是动态创建<script>标签并将其添加到 HTML 文档中。这种方式可以使脚本在页面加载过程中异步下载和执行,不会阻塞页面的其他内容的呈现。
function loadScript(url) {
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = url; 
  document.head.appendChild(script); 
}
// 调用函数加载外部脚本
loadScript('your_script.js'); 
  • 动态加载的脚本与普通的脚本一样,也可以使用deferasync属性来进一步控制其加载和执行行为(但需要注意浏览器的兼容性,本文不细述)
    • 如果添加了defer属性,浏览器会在文档解析完成后,按照脚本在文档中出现的顺序依次执行它们。
    • 如果添加了async属性,脚本会在下载完成后尽快执行,而不考虑文档的解析顺序,执行时机可能在DOMContentLoaded事件之前或之后。

本文参考链接: zhuanlan.zhihu.com/p/622763093