<script defer =“defer”>究竟是如何工作的?</script>

时间:2011-03-09 18:29:32

标签: javascript html 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>

但是,它会提醒2 - 1 - 3。为什么不警告1 - 2 - 3

11 个答案:

答案 0 :(得分:162)

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

  

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


  

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


  

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


  

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

     

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

答案 1 :(得分:155)

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

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

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

延迟执行相同的操作,除了声明保证脚本按照页面上指定的顺序执行,以及它们将在文档完成解析后执行。因此,某些脚本可能会完成下载,然后等待稍后下载但出现在它们之前的脚本。

不幸的是,由于真正的标准猫战斗,延迟的定义根据规格变化规格,即使在最近的规格中也没有提供有用的保证。正如答案herethis issue所示,浏览器实施的方式不同:

  • 在某些情况下,某些浏览器会出现导致defer脚本无序运行的错误。
  • 有些浏览器将DOMContentLoaded事件延迟到加载defer脚本之后,有些浏览器没有。
  • 某些浏览器在defer个元素上使用内联代码并且没有<script>属性服从src,有些浏览器会忽略它。

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

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

全球98%的浏览器和美国的99%使用此方法都会阻止。

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

答案 2 :(得分:50)

更新时间: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比人们更频繁地遵循标准,实际上“延迟”属性在DOM Level 1规范中定义http://www.w3.org/TR/REC-DOM-Level-1/level-one-html.html

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

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

答案 3 :(得分:13)

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

答案 4 :(得分:6)

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

答案 5 :(得分:6)

因为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>

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

答案 6 :(得分:4)

还应注意,在某些情况下使用script defer时,IE&lt; = 9可能存在问题。更多相关信息:https://github.com/h5bp/lazyweb-requests/issues/42

答案 7 :(得分:4)

请参阅2013年撰写的Google开发人员Jake Archibald撰写的这篇优秀文章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”的脚本上忽略“延迟”。

     

IE&lt; 10说:我可能会在执行1.js的过程中执行2.js。不是很有趣吗?

     

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

     

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

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

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

答案 8 :(得分:1)

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

延迟适用于chrome,firefox,即&gt; 7和Safari

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

答案 9 :(得分:0)

defer属性是一个布尔属性。

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

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

注意:有几种方法可以执行外部脚本:

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

答案 10 :(得分:0)