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