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

pim*_*vdb 201 html javascript deferred-execution

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

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

<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>
Run Code Online (Sandbox Code Playgroud)

但是,它提醒2 - 1 - 3.为什么不提醒1 - 2 - 3

Alo*_*hci 162

来自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属性,并且该元素已标记为"parser-inserted",并且该元素没有async属性:

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

  • 也许,我的回答会阻止人们对我的回答进行投票,因为你的评论没有帮助.接受的答案没有错,答案是不同的,因为在2011年初,HTML5规范与主流网络浏览器的相关性不如目前.这个答案可能会更好,但接受的答案不是*错*任何标准. (37认同)
  • 虽然了解规范的内容很有用,但事实证明,某些[IE浏览器如<9实现`延迟'严重](https://github.com/h5bp/lazyweb-requests/issues/42).如果使用`defer`,则不能依赖于某些浏览器中按顺序执行的脚本文件. (3认同)
  • @Flimm不仅仅是IE,似乎[在Firefox中也无法保证执行顺序](http://stackoverflow.com/questions/32413279/defer-scripts-and-execution-order-on-browsers). (2认同)

Chr*_*ini 155

真正的答案是:因为你不能相信推迟.

在概念上,延迟和异步的区别如下:

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

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

不幸的是,由于什么是真正的标准猫斗争,推迟的定义根据规格变化规格,即使在最近的规格中也没有提供有用的保证.作为这里的答案和此问题的演示,浏览器实现的推迟不同:

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

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

<script defer async src="..."></script>
Run Code Online (Sandbox Code Playgroud)

全球98%的浏览器和美国的99%将使用这种方法避免阻塞.

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

  • 谢谢,你的回答对我最有帮助! (13认同)
  • 我认为这是不正确的.延迟的好处是它在页面解析完成之前不会执行.这个页面有一个很好的视觉来解释异步和延迟之间的区别:http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/ (5认同)

Mar*_*p51 50

更新日期:2016年2月19日

考虑这个答案已经过时了.有关较新浏览器版本的信息,请参阅此帖子上的其他答案.


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

延迟属性特定于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 Level 1规范中定义http://www.w3.org/TR/REC-DOM-Level-1/level -酮中将Html.HTML

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

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

  • @MarkAtRamp51 - 如果你的答案已经过时,你应该编辑它而不是抱怨其他答案的评论中的downvotes.Downvotes用于"无用"的答案. (8认同)
  • @ChristianConkle我很欣赏礼仪课程,但是这里的其他答案都是最新的.我正在解决在提出问题时未选择错误答案的事实.也许你应该警告人们传播关于社区不正确地选择答案的错误评估,而不是人们试图提醒人们事情随着时间的推移而变化,而且背景很重要.我不认为删除我的答案是有价值的,因为历史信息也是有价值的. (8认同)
  • @Leo应该不被标记呢?通过搜索"html5 defer script",这是谷歌的第三个结果.这个答案就是为很多用户提供了过时且不正确的定义.(当前定义:"表示用户代理可以推迟脚本的处理.请参阅HTML 4.0中的延迟属性定义."). (3认同)
  • "我没有看到删除我的答案的价值,因为历史信息也是有价值的"在这种情况下,如何在开头添加一个注释,指出它只适用于HTML5之前,然后链接到"正确"(最新的答案?这应该可以为你节省很多麻烦(作为一个曾经接受过"错误"答案的人说话,并且"同伴压力"最终会改变它). (2认同)
  • @MarkAtRamp51我认为你应该更新你的答案.任何人发现这个问题,所以你的答案将无法识别其历史信息.它会让他们看起来像是今天正确的答案.这就是互联网的运作方式.所以你应该编辑你的答案,注意它曾经是正确的,并参考正确的答案. (2认同)

Raj*_*aul 13

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


Bij*_*pam 8

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

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

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


Sou*_*tra 6

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


Fli*_*imm 6

看看这篇优秀的文章深入探讨脚本加载的黑暗水域Google 开发人员 Jake Archibald 在 2013 年撰写的

\n

引用该文章的相关部分:

\n
\n

推迟

\n
<script src="//other-domain.com/1.js" defer></script>\n<script src="2.js" defer></script>\n
Run Code Online (Sandbox Code Playgroud)\n

规范说:一起下载,在 DOMContentLoaded 之前按顺序执行。在没有 \xe2\x80\x9csrc\xe2\x80\x9d 的脚本上忽略 \xe2\x80\x9cdefer\xe2\x80\x9d。

\n

IE < 10 表示:我可能会在执行 1.js 的过程中执行 2.js。\xe2\x80\x99 是不是很有趣?

\n

红色的浏览器:我不知道 \xe2\x80\x9cdefer\xe2\x80\x9d 是什么,我\xe2\x80\x99m 将加载脚本,就好像它不在 \xe2\x80\x99t 那里一样。

\n

其他浏览器说:好的,但我可能不会在没有 \xe2\x80\x9csrc\xe2\x80\x9d 的脚本上忽略 \xe2\x80\x9cdefer\xe2\x80\x9d 。

\n
\n

(根据此评论,我将补充一点,早期版本的 Firefox 在defer脚本完成运行之前会触发 DOMContentLoaded 。)

\n

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

\n


小智 5

<script defer> -
\n一旦浏览器与带有 defer 的 script 标签交互

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

<script async> \xe2\x80\x94
\n一旦浏览器与 async 交互脚本标签

\n
    \n
  1. 它开始获取脚本文件,同时并行解析 HTML。
  2. \n
  3. 但是当脚本完全获取时停止 HTML 解析,之后开始执行脚本,之后 HTML 解析继续
  4. \n
\n

<script> \xe2\x80\x94
\n当浏览器与脚本标签交互时

\n
    \n
  1. 它停止 HTML 的解析,获取脚本文件,
  2. \n
  3. 在本例中,执行脚本,然后继续解析 HTML
  4. \n
\n