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

Posted

技术标签:

【中文标题】<script defer="defer"> 究竟是如何工作的?【英文标题】:How exactly does <script defer="defer"> work? 【发布时间】:2011-07-12 03:31:49 【问题描述】:

我有几个&lt;script&gt; 元素,其中一些元素中的代码依赖于其他&lt;script&gt; 元素中的代码。我看到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

【问题讨论】:

也许可以查看this article。而且,与往常一样,IE 对某事的含义有自己的看法,并决定先加载脚本,但延迟执行,直到加载正文(通常)。 我想你会发现 IE 对 defer 的定义符合 W3C 在 DOM Level 1 规范中对 defer 的意图。 正如 Alohci 在他的回答中已经指出的那样,根据 html Standard defer 仅在指定 src 时有效。这可能是您的示例在大多数浏览器中无法按预期工作的原因。 @Pankrat 真实故事!试试jsfiddle.net/xXZMN/50 在 Firefox24 中测试 发现这张图对理解延迟以及异步和延迟之间的区别非常有帮助:growingwiththeweb.com/2014/02/async-vs-defer-attributes.html 【参考方案1】:

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

defer 和 async 属性必须 如果 src 属性不指定 不存在。


共有三种可能的模式 可以使用这些选择 属性 [异步和延迟]。如果 async 属性是 存在,那么脚本将是 异步执行,只要它 可用。如果 async 属性 不存在,但有 defer 属性 存在,那么脚本是 当页面完成时执行 解析。如果两个属性都不 存在,然后获取脚本 并立即执行,在 用户代理继续解析页面。


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


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

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

【讨论】:

也许,由于您的评论没有帮助,我在这里的回复会阻止人们对我的回答投反对票。公认的答案没有错,答案是不同的,因为在 2011 年初,HTML5 规范与主流网络浏览器的相关性不如现在。这个答案可能会更好,但接受的答案在任何标准上都不是错误 虽然了解规范的内容很有用,但事实证明有些browsers like IE<9 implement defer badly。如果使用defer,则不能依赖在某些浏览器中按顺序执行的脚本文件。 @Flimm 不只是IE,看来the execution order is not guaranteed in Firefox either。 第一个报价不再有效,对吧?现在我可以读到:“如果 src 属性不存在,或者脚本不是经典脚本,则不得指定该属性。”。经典脚本也是没有 src="" 的。【参考方案2】:

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

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

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

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

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

在某些情况下,某些浏览器存在导致defer 脚本无序运行的错误。 有些浏览器会延迟 DOMContentLoaded 事件,直到 defer 脚本加载完毕,有些则不会。 有些浏览器在 &lt;script&gt; 元素上遵守 defer 内联代码但没有 src 属性,有些则忽略它。

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

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

全球 98% 的浏览器和美国 99% 的浏览器将避免使用这种方法进行屏蔽。

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

【讨论】:

谢谢,您的回答对我最有帮助! 我认为这是不正确的。 defer 的好处是它在页面解析完成之前不会执行。这个页面有一个很好的视觉来解释异步和延迟之间的区别:peter.sh/experiments/… @tinkerr 在概念上你是正确的;实际上,事实并非如此。因为它没有一致地实施,所以顺序保证不是通用的,因此不是保证。在实施某些事情时,您关心执行。设计的意图很可爱,但并不是特别有用。 只是想指出 Opera 从 version 15 开始支持 defer 属性,该属性是在 June 2nd, 2013 上发布的。 @VikasBansal 适用于不支持异步的旧版浏览器 - 即旧版 IE。【参考方案3】:

更新日期:2016 年 2 月 19 日

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


基本上,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 遵循标准的频率比人们想象的要多,实际上“defer”属性是在 DOM Level 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”),因此,用户代理可以继续解析并渲染。”

【讨论】:

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

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

【讨论】:

【参考方案5】:

因为 defer 属性仅适用于带有 src 的脚本标签。找到了一种模拟内联脚本延迟的方法。使用 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 事件在延迟属性脚本完全加载后触发。

【讨论】:

【参考方案6】:

defer 属性仅适用于外部脚本(仅应在存在 src 属性时使用)。

【讨论】:

【参考方案7】:

看看这篇由 Google 开发人员 Jake Archibald 在 2013 年撰写的优秀文章 Deep dive into the murky waters of script loading。

引用那篇文章的相关部分:

推迟

<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>

规范说:一起下载,在 DOMContentLoaded 之前按顺序执行。忽略没有“src”的脚本上的“defer”。

IE:我可能会在1.js执行到一半的时候执行2.js。不好玩吗??

The browsers in red说:我不知道这个“延迟”是什么,我要加载脚本,就好像它不存在一样。

其他浏览器说:好的,但我可能不会忽略没有“src”的脚本上的“defer”。

(我将添加早期版本的 Firefox 在 defer 脚本完成运行之前触发 DOMContentLoaded,according to this comment。)

现代浏览器似乎正确地支持async,但您需要接受无序运行的脚本并且可能在 DOMContentLoaded 之前运行。

【讨论】:

【参考方案8】:

还应注意,在某些情况下使用script defer 时,IEhttps://github.com/h5bp/lazyweb-requests/issues/42

【讨论】:

【参考方案9】:

设置此布尔属性以向浏览器指示脚本将在文档被解析后执行。由于所有其他主要浏览器尚未实现此功能,因此作者不应假设脚本的执行实际上会被推迟。永远不要从延迟脚本调用 document.write() (从 Gecko 1.9.2 开始,这会吹走文档)。不应该在没有 src 属性的脚本上使用 defer 属性。从 Gecko 1.9.2 开始,没有 src 属性的脚本会忽略 defer 属性。但是,在 Gecko 1.9.1 中,如果设置了 defer 属性,甚至内联脚本也会被延迟。

defer 适用于 chrome , firefox , ie > 7 和 Safari

参考:https://developer.mozilla.org/en-US/docs/HTML/Element/script

【讨论】:

【参考方案10】:

defer 属性是一个布尔属性。

如果存在,它指定在页面完成解析时执行脚本。

注意:defer 属性仅适用于外部脚本(仅应在存在 src 属性时使用)。

注意:可以通过多种方式执行外部脚本:

如果存在异步:脚本与页面的其余部分异步执行(脚本将在页面继续解析时执行) 如果 async 不存在且 defer 存在:当页面完成解析时执行脚本 如果 async 或 defer 都不存在:在浏览器继续解析页面之前立即获取并执行脚本

【讨论】:

【参考方案11】:

- 只要浏览器使用 defer 与 script 标签交互

    它开始获取脚本文件,同时并行解析 HTML。 在这种情况下,脚本只有在 HTML 解析完成后才会执行。

— 一旦浏览器与异步脚本标签交互

    它在并行解析 HTML 时开始获取脚本文件。 但是当脚本完全获取时停止 HTML 解析,之后它开始执行脚本,之后 HTML 解析继续

— 一旦浏览器与脚本标签交互

    它停止解析 HTML,获取脚本文件, 在这种情况下,执行脚本,然后继续解析 HTML

【讨论】:

以上是关于<script defer="defer"> 究竟是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

JS - defer 和 async

script 异步加载 async 和 defer的区别

js延迟加载的方式都有哪些?

jsp中不执行script

script标签中defer和async属性的区别

延迟加载JS