<script defer="defer"> 究竟是如何工作的?

IT技术 javascript html deferred-execution
2021-02-06 07:22:04

我有几个<script>元素,其中一些元素中的代码依赖于其他<script>元素中的代码我看到该defer属性在这里可以派上用场,因为它允许代码块在执行中被推迟。

为了测试它,我在 Chrome 上执行了这个:http : //jsfiddle.net/xXZMN/

<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>

但是,它会发出警报2 - 1 - 3为什么不提醒1 - 2 - 3

6个回答

HTML5 规范中的一些片段:http : //w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async

如果 src 属性不存在,则不能指定 defer 和 async 属性。


可以使用这些属性 [async 和 defer] 选择三种可能的模式。如果存在 async 属性,则脚本将在可用时异步执行。如果 async 属性不存在但 defer 属性存在,则在页面完成解析时执行脚本。如果这两个属性都不存在,则在用户代理继续解析页面之前,将立即获取并执行脚本。


由于历史原因,这些属性的确切处理细节有些重要,涉及 HTML 的许多方面。因此,实施要求必然分散在整个规范中。下面的算法(在本节中)描述了这个处理的核心,但这些算法引用并被 HTML 中的脚本开始和结束标记的解析规则、外部内容和 XML 中的 document.write 规则所引用() 方法,脚本的处理等。


如果元素具有 src 属性,并且元素具有 defer 属性,并且该元素已被标记为“解析器插入”,并且该元素没有 async 属性:

该元素必须添加到脚本列表的末尾,当文档完成与创建该元素的解析器的 Document 相关联的解析时,这些脚本将执行。

虽然了解规范的内容很有用,但事实证明,某些浏览器(如 IE<9)的实现defer很糟糕如果使用defer,则不能依赖于在某些浏览器中按顺序执行的脚本文件。
2021-03-25 07:22:04
也许,由于您的评论没有帮助,我在这里的回复会阻止人们对我的答案投反对票。接受的答案并没有错,答案是不同的,因为在 2011 年初,HTML5 规范与主流网络浏览器的相关性不如现在。这个答案可能会更好,但接受的答案按任何标准都没有
2021-03-28 07:22:04
第一个报价不再有效,对吗?现在我可以读到:“如果 src 属性不存在,或者脚本不是经典脚本,则不得指定该属性。”。经典脚本也是没有 src="" 的脚本。
2021-03-31 07:22:04
@Flimm 不仅仅是 IE,似乎在 Firefox 中也不能保证执行顺序
2021-04-10 07:22:04

真正的答案是:因为你不能相信 defer。

在概念上,defer 和 async 的区别如下:

async允许在后台下载脚本而不会阻塞。然后,在它完成下载的那一刻,渲染被阻止并执行该脚本。脚本执行后,渲染恢复。

defer做同样的事情,除了保证脚本按照它们在页面上指定的顺序执行,并且它们将在文档完成解析后执行。因此,某些脚本可能完成下载然后坐等稍后下载但出现在它们之前的脚本。

不幸的是,由于真正是标准的猫战,defer 的定义因规范而异,即使在最新的规范中也不能提供有用的保证。正如这里的答案这个问题所展示的,浏览器实现 defer 的方式不同:

  • 在某些情况下,某些浏览器会存在导致defer脚本乱序运行的错误
  • 一些浏览器将DOMContentLoaded事件延迟defer脚本加载之后,而有些则不会。
  • 有些浏览器服从defer<script>与内嵌代码,并没有元素src属性,有些忽略它。

幸运的是,规范至少指定了异步覆盖延迟。因此,您可以将所有脚本视为异步并获得广泛的浏览器支持,如下所示:

<script defer async src="..."></script>

全球 98% 的浏览器和美国 99% 的浏览器将通过这种方法避免阻止。

(如果您需要等到文档完成解析,请收听事件DOMContentLoaded事件或使用 jQuery 的便捷.ready()功能。无论如何您都希望这样做,以便在根本没有实现的浏览器上优雅地回退defer。)

@tinkerr 在概念上你是正确的;实际上,事实并非如此。因为它没有一致地实现,所以序列保证不是通用的,因此不是保证。在实施某些事情时,您关心的是执行。设计的意图很可爱,但并不是特别有用。
2021-03-23 07:22:04
谢谢,你的回答对我最有帮助!
2021-03-24 07:22:04
@VikasBansal 对于不支持异步的旧浏览器 - 即旧 IE。
2021-03-25 07:22:04
我相信这是不正确的。defer 的好处是它在页面解析完成之前不会执行。这个页面有一个很好的视觉效果来解释 async 和 defer 之间的区别:peter.sh/experiments/...
2021-04-01 07:22:04
只是想指出 Operadefer2013 年6 月 2 日发布的版本 15开始支持该属性
2021-04-11 07:22:04

更新:2/19/2016

认为这个答案已经过时了。有关与较新浏览器版本相关的信息,请参阅此帖子上的其他答案。


基本上, defer 告诉浏览器在执行该脚本块中的 javascript 之前等待“直到它准备好”。通常这是在 DOM 完成加载并且 document.readyState == 4 之后

defer 属性特定于 Internet Explorer。在 Internet Explorer 8 中,在 Windows 7 上,我在您的 JS Fiddle 测试页中看到的结果是 1 - 2 - 3。

结果可能因浏览器而异。

http://msdn.microsoft.com/en-us/library/ms533719(v=vs.85).aspx

与流行的看法相反,IE 遵循标准的频率比人们所承认的要多,实际上“延迟”属性是在 DOM 级别 1 规范中定义的http://www.w3.org/TR/REC-DOM-Level-1/level -one-html.html

W3C 对 defer 的定义:http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer

“设置后,这个布尔属性向用户代理提供了一个提示,即脚本不会生成任何文档内容(例如,javascript 中没有“document.write”),因此,用户代理可以继续解析和呈现。”

@Leo 那么它不应该被标记吗?通过搜索“html5 defer script”,这是谷歌的第三个结果。然后,这个答案为许多用户提供了过时且不正确的定义。(当前定义:“表示用户代理可以延迟处理脚本。参见 HTML 4.0 中的延迟属性定义。”)。
2021-03-20 07:22:04
“我认为删除我的答案没有value,因为历史信息也很有value”在这种情况下,如何在开头添加注释,指出它仅适用于 HTML5 之前的版本,然后链接到“正确”(最新)答案?这应该会为您省去很多麻烦(作为一个也接受过一次“错误”答案的人来说,并且“受到同伴的压力”最终要改变它)。
2021-03-26 07:22:04
@MarkAtRamp51 - 如果您的答案已过时,您应该对其进行编辑,而不是在对其他答案的评论中抱怨被否决。反对票是针对“无用”的答案。
2021-03-27 07:22:04
@ChristianConkle 我很欣赏礼仪课,但是这里的其他答案是最新的。我正在解决这样一个事实,即在提出问题时没有选择错误的答案。也许你应该监管那些散布关于社区不正确选择答案的错误评估的人,而不是那些试图提醒人们事情会随着时间而变化的人,而背景很重要。我认为删除我的答案没有value,因为历史信息也很有value。
2021-04-04 07:22:04
@MarkAtRamp51我认为你应该更新你的答案。任何发现此问题的人以及您的答案都不会识别其历史信息。在他们看来,这就是今天正确的答案。这就是互联网的运作方式。所以你应该编辑你的答案,注意它曾经是正确的,并参考正确的答案。
2021-04-10 07:22:04

defer只能在<script>标记中用于外部脚本包含。因此,建议在<script>-section 的 -tags 中使用<head>

由于延迟属性仅适用于带有 src 的脚本标记。找到了一种模拟内联脚本 defer 的方法。使用 DOMContentLoaded 事件。

<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
    // Your inline scripts which uses methods from external-scripts.
});
</script>

这是因为 DOMContentLoaded 事件在延迟属性脚本完全加载后触发。