如果您没有动态加载脚本或将它们标记为defer
or async
,则脚本将按照页面中遇到的顺序加载。无论是外部脚本还是内联脚本都没有关系 - 它们按照在页面中遇到的顺序执行。外部脚本之后的内联脚本会一直保留,直到它们之前的所有外部脚本都已加载并运行。
异步脚本(无论它们如何指定为异步)以不可预知的顺序加载和运行。浏览器并行加载它们,并且可以按照它想要的任何顺序自由运行它们。
多个异步事物之间没有可预测的顺序。如果需要一个可预测的顺序,则必须通过注册来自异步脚本的加载通知并在加载适当的内容时手动排序 javascript 调用来对其进行编码。
当动态插入脚本标签时,执行顺序的行为将取决于浏览器。您可以在这篇参考文章中了解 Firefox 的行为方式。简而言之,新版本的 Firefox 默认将动态添加的脚本标签设置为异步,除非另外设置了脚本标签。
脚本标签async
可以在加载后立即运行。实际上,浏览器可能会暂停解析器正在执行的任何其他操作并运行该脚本。因此,它几乎可以随时运行。如果脚本被缓存,它可能几乎立即运行。如果脚本需要一段时间才能加载,它可能会在解析器完成后运行。要记住的一件事async
是它可以随时运行,而且时间是不可预测的。
脚本标记defer
等待整个解析器完成,然后defer
按照遇到的顺序运行所有标记为的脚本。这允许您将多个相互依赖的脚本标记为defer
. 它们都将被推迟到文档解析器完成之后,但它们将按照遇到的顺序执行,保留它们的依赖关系。我认为defer
脚本被放入一个队列中,解析器完成后将处理该队列。从技术上讲,浏览器可能随时在后台下载脚本,但它们不会执行或阻止解析器,直到解析器完成对页面的解析并解析和运行任何未标记的内联脚本defer
或async
.
这是那篇文章的引述:
插入脚本的脚本在 IE 和 WebKit 中异步执行,但在 Opera 和 4.0 之前的 Firefox 中同步执行。
HTML5 规范(适用于较新的兼容浏览器)的相关部分在此处。那里有很多关于异步行为的文章。显然,此规范不适用于您可能需要测试才能确定其行为的旧浏览器(或不符合标准的浏览器)。
引用 HTML5 规范:
然后,必须遵循以下描述情况的第一个选项:
如果元素具有 src 属性,并且该元素具有 defer 属性,并且该元素已被标记为“parser-inserted”,并且该元素没有 async 属性
,则该元素必须添加到列表的末尾当文档完成与创建元素的解析器的文档相关联的解析时将执行的脚本。
一旦获取算法完成,网络任务源放置在任务队列中的任务必须设置元素的“准备好被解析器执行”标志。解析器将处理执行脚本。
如果该元素具有 src 属性,并且该元素已被标记为“解析器插入”,并且该元素没有异步属性
该元素是创建该元素的解析器的 Document 的待处理解析阻止脚本。(每个文档一次只能有一个这样的脚本。)
一旦获取算法完成,网络任务源放置在任务队列中的任务必须设置元素的“准备好被解析器执行”标志。解析器将处理执行脚本。
如果该元素没有 src 属性,并且该元素已被标记为“已插入解析器”,并且创建脚本元素的 HTML 解析器或 XML 解析器的文档具有阻止脚本的样式表该元素是创建元素的解析器的 Document 的待处理解析阻止脚本。(每个文档一次只能有一个这样的脚本。)
设置元素的“准备好被解析器执行”标志。解析器将处理执行脚本。
如果元素具有 src 属性,没有 async 属性,并且没有设置“force-async”标志,则必须将元素添加到将按顺序执行的脚本列表的末尾尽快关联在准备脚本算法开始时使用脚本元素的文档。
一旦获取算法完成,网络任务源放置在任务队列中的任务必须运行以下步骤:
如果该元素现在不是脚本列表中的第一个元素,它将按照上面添加的顺序尽快执行,则将该元素标记为准备就绪,但在不执行脚本的情况下中止这些步骤。
执行:执行该脚本列表中第一个脚本元素对应的脚本块,该脚本将尽快按顺序执行。
从此脚本列表中删除将尽快按顺序执行的第一个元素。
如果这个将尽快按顺序执行的脚本列表仍然不为空,并且第一个条目已经被标记为就绪,则跳回到标记为执行的步骤。
如果元素具有 src 属性,则该元素必须在准备脚本算法开始时添加到脚本元素的 Document 中尽快执行的脚本集。
一旦获取算法完成,网络任务源放置在任务队列中的任务必须执行脚本块,然后从将尽快执行的脚本集中删除元素。
否则用户代理必须立即执行脚本块,即使其他脚本已经在执行。
Javascript 模块脚本type="module"
呢?
Javascript 现在支持使用如下语法加载模块:
<script type="module">
import {addTextToBody} from './utils.mjs';
addTextToBody('Modules are pretty cool.');
</script>
或者,带有src
属性:
<script type="module" src="http://somedomain.com/somescript.mjs">
</script>
所有带有的脚本都会type="module"
自动赋予该defer
属性。这将与页面的其他加载并行(如果不是内联)下载它们,然后按顺序运行它们,但在解析器完成之后。
模块脚本也可以被赋予async
将尽快运行内联模块脚本的属性,而不是等到解析器完成并且不等待以async
相对于其他脚本的任何特定顺序运行脚本。
有一个非常有用的时间线图,显示了不同脚本组合的获取和执行,包括本文中的模块脚本:Javascript 模块加载。