脚本标签 - 异步&延缓

时间:2012-05-29 23:28:01

标签: javascript html5

我对async&amp;属性有几个问题。 defer标记的<script>,据我所知,仅适用于HTML5浏览器。

我的一个网站有两个外部JavaScript文件,目前位于</body>标记的上方;第一个是来自谷歌的,第二个是本地外部脚本。

关于网站加载速度

  1. async添加到页面底部的两个脚本中是否有任何优势?

  2. async选项添加到两个脚本并将它们放在<head>页面的顶部是否有任何好处?

  3. 这是否意味着他们在页面加载时下载?
  4. 我认为这会导致HTML4浏览器出现延迟,但会加速HTML5浏览器的页面加载吗?
  5. 使用<script defer src=...

    1. 使用属性<head>加载defer内的两个脚本与使用</body>之前的脚本具有相同的效果吗?
    2. 我再次假设这会降低HTML4浏览器的速度。
    3. 使用<script async src=...

      如果我有两个启用了async的脚本

      1. 他们会同时下载吗?
      2. 或者与页面其余部分一次一个?
      3. 脚本的顺序是否会成为问题?例如,一个脚本依赖于另一个脚本,因此如果下载速度更快,则第二个脚本可能无法正确执行等。
      4. 最后,我最好保留原样,直到HTML5更常用?

11 个答案:

答案 0 :(得分:565)

此图片解释了正常的脚本标记,async和defer

enter image description here

  • 一旦加载脚本就会执行异步脚本,所以它就会执行 不保证执行顺序(你包含的脚本) 结束可以在第一个脚本文件之前执行)

  • 延迟脚本保证它们出现的执行顺序 在页面中。

参考此链接:http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

答案 1 :(得分:334)

</body>之前保持您的脚本正确。在某些情况下,Async可以与位于那里的脚本一起使用(参见下面的讨论)。 Defer不会对位于那里的脚本产生太大影响,因为DOM解析工作已经完成了很多工作。

这篇文章解释了异步和延迟之间的区别:http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/

如果您在</body>之前将脚本保留在正文末尾,那么您的HTML将在旧浏览器中更快地显示。因此,为了保持旧版浏览器的加载速度,您不希望将它们放在其他任何位置。

如果您的第二个脚本依赖于第一个脚本(例如,您的第二个脚本使用第一个脚本中加载的jQuery),那么您无法在没有其他代码来控制执行顺序的情况下使它们异步,但您可以让它们延迟,因为延迟脚本仍将按顺序执行,直到文档解析完毕为止。如果您拥有该代码并且不需要立即运行脚本,则可以使它们异步或延迟。

您可以将脚本放在<head>标记中并将它们设置为defer,并且将延迟加载脚本,直到解析DOM并在新浏览器中显示快速页面支持延迟,但它在旧版浏览器中根本不会帮助你,它实际上并不比仅在</body>之前放置脚本在所有浏览器中都有效。所以,你可以看到为什么最好把它们放在</body>之前。

当您在脚本加载时并不关心时,Async会更有用,并且用户所依赖的任何其他内容都不依赖于该脚本加载。最常被引用的使用异步的示例是像Google Analytics这样的分析脚本,您不需要等待任何事情,并且不急于即将运行并且它是独立的,因此没有其他任何依赖它。

通常jQuery库不适合async,因为其他脚本依赖它并且你想安装事件处理程序,所以你的页面可以开始响应用户事件,你可能需要运行一些基于jQuery的初始化代码来建立页面的初始状态。它可以用作异步,但是其他脚本必须编码才能在加载jQuery之前执行。

答案 2 :(得分:190)

HTML5:asyncdefer

在HTML5中,您可以告诉浏览器何时运行JavaScript代码。有三种可能性:

<script       src="myscript.js"></script>

<script async src="myscript.js"></script>

<script defer src="myscript.js"></script>
  1. 如果没有asyncdefer,浏览器会立即运行您的脚本,然后再渲染脚本标记下方的元素。

  2. 使用async(异步),浏览器将继续加载HTML页面并在浏览器加载时呈现它并同时执行脚本。

  3. 使用defer,浏览器将在页面完成解析后运行您的脚本。 (不必完成下载所有图像文件。这很好。)

答案 3 :(得分:23)

asyncdefer脚本都会立即开始下载,而不会暂停解析器,并且都支持可选的onload处理程序,以满足执行初始化的常见需求,这取决于脚本。< / p>

asyncdefer之间的差异在于脚本执行时的中心。每个async脚本在完成下载之后和窗口的加载事件之前的第一次机会执行。这意味着async脚本可能(并且可能)不按页面中出现的顺序执行。而另一方面,defer脚本保证按照它们在页面中出现的顺序执行。解析完成后,但在文档的DOMContentLoaded事件之前,执行开始。

来源&amp;更多详情:here

答案 4 :(得分:20)

面对同样的问题,现在清楚地了解两者是如何运作的。希望这个参考链接会有所帮助......

<强> 异步

将async属性添加到脚本标记时,将发生以下情况。

<script src="myfile1.js" async></script>
<script src="myfile2.js" async></script>
  1. 提出并行请求以获取文件。
  2. 继续解析文档,就像它从未中断一样。
  3. 下载文件时执行各个脚本。
  4. <强> 推迟

    Defer非常类似于一个主要差异的异步。以下是浏览器遇到带有defer属性的脚本时发生的情况。

    <script src="myfile1.js" defer></script>
    <script src="myfile2.js" defer></script>
    
    1. 提出并行请求以获取单个文件。
    2. 继续解析文档,就像它从未中断一样。
    3. 即使脚本文件已下载,也要完成解析文档。
    4. 按照文档中遇到的顺序执行每个脚本。
    5. 参考:Difference between Async and Defer

答案 5 :(得分:2)

我认为,杰克·阿奇博尔德(Jake Archibald)早在2013年就向我们提供了一些见解,这些见解可能会进一步增加对该主题的积极性:

https://www.html5rocks.com/en/tutorials/speed/script-loading/

  

圣杯正在立即下载一组脚本,而不会阻止渲染,并按添加顺序尽快执行。不幸的是HTML讨厌您,不会让您这样做。

     

(...)

     

答案实际上是在HTML5规范中的,尽管它隐藏在脚本加载部分的底部。   “ 异步IDL属性控制元素是否将异步执行。如果设置了元素的” force-async“标志,则异步IDL属性在获取时必须返回true,在设置时必须返回” force -async“标志必须首先取消设置... ”。

     

(...)

     

动态创建并添加到文档中的脚本默认情况下是异步的,它们不会阻止渲染并在下载后立即执行,这意味着它们可能以错误的顺序出现。但是,我们可以将它们明确标记为不异步:

[
    '//other-domain.com/1.js',
    '2.js'
].forEach(function(src) {
    var script = document.createElement('script');
    script.src = src;
    script.async = false;
    document.head.appendChild(script);
});
  

这使我们的脚本混合了普通HTML无法实现的行为。由于脚本明显不异步,因此脚本被添加到执行队列中,与我们在第一个纯HTML示例中添加的脚本相同。但是,通过动态创建它们,它们可以在文档解析之外执行,因此在下载它们时不会阻止渲染(不要将非异步脚本加载与sync XHR混淆,这从来都不是一件好事)。 / p>      

上面的脚本应内联包含在页面的开头,请尽快对脚本下载进行排队,而不会破坏渐进式渲染,并应按指定的顺序尽快执行。 “ 2.js”可以在“ 1.js”之前免费下载,但是要等到“ 1.js”成功下载并执行或无法执行后才能执行。欢呼! 异步下载但有序执行

仍然,这可能不是加载脚本的最快方法:

  

(...)在上面的示例中,浏览器必须解析并执行脚本以发现要下载的脚本。这会将脚本隐藏在预加载扫描仪中。浏览器使用这些扫描仪发现您下一步可能访问的页面上的资源,或者在解析器被其他资源阻止时发现页面资源。

     

我们可以通过将可发现性放在文档的开头来重新添加可发现性:

<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">
  

这告诉浏览器页面需要1.js和2.js。 link [rel = subresource]与link [rel = prefetch]类似,但语义不同。不幸的是,Chrome浏览器目前仅支持该脚本,您必须声明要加载两次的脚本,一次是通过链接元素,一次是在脚本中。

     

更正:我最初说的是这些是由预加载扫描仪拾取的,不是,它们是由常规解析器拾取的。但是,预加载扫描程序可能无法进行预加载,而可执行代码中包含的脚本则永远无法预加载。感谢Yoav Weiss在评论中纠正了我。

答案 6 :(得分:0)

似乎延迟和异步的行为依赖于浏览器,至少在执行阶段。注意,延迟仅适用于外部脚本。我假设异步遵循相同的模式。

在IE 11及以下版本中,顺序似乎如下:

  • async(可在页面加载时部分执行)
  • none(可以在页面加载时执行)
  • 延迟(在页面加载后执行,所有按照文件中的放置顺序延迟)

在Edge,Webkit等中,async属性似乎被忽略或放在最后:

  • data-pagespeed-no-defer(在加载页面之前,在任何其他脚本之前执行)
  • none(可在页面加载时执行)
  • 延迟(等待加载DOM,所有按照文件中的放置顺序延迟)
  • 异步(似乎要等到DOM加载)

在较新的浏览器中,data-pagespeed-no-defer属性在任何其他外部脚本之前运行。这适用于不依赖于DOM的脚本。

注意:当您需要明确执行外部脚本的顺序时,请使用延迟。这告诉浏览器按照放置在文件中的顺序执行所有延迟脚本。

ASIDE:外部javascripts的大小在加载时确实很重要......但对执行顺序没有影响。

如果您担心脚本的性能,可能需要考虑缩小或简单地使用XMLHttpRequest动态加载它们。

答案 7 :(得分:0)

渲染引擎经过几个步骤,直到它在屏幕上绘制任何东西。

看起来像这样:

  1. 根据我们为文档设置的编码将 HTML 字节转换为字符;
  2. 令牌是根据字符创建的。令牌意味着分析字符并指定开端和嵌套标签;
  3. 从令牌分离节点被创建。它们是对象,根据标记化过程传递的信息,引擎创建对象,其中包含有关每个节点的所有必要信息;
  4. 在创建 DOM 之后。 DOM 是树状数据结构,代表整个层次结构以及有关标签关系和规范的信息;

同样的过程也适用于 CSS。 CSS 渲染引擎为 CSS 创建不同/分离的数据结构,但它被称为 CSSOM(CSS 对象模型)

浏览器仅适用于对象模型,因此它需要了解有关 DOM 和 CSSDOM 的所有信息。

下一步是以某种方式结合 DOM 和 CSSOM。因为没有 CSSOM 浏览器不知道如何在渲染过程中为每个元素设置样式。

以上所有信息意味着,您在 html(javascript、css)浏览器中提供的任何内容都将暂停 DOM 构建过程。如果您熟悉事件循环,那么事件循环如何执行任务有一个简单的规则:

  1. 执行宏任务;
  2. 执行微任务;
  3. 渲染;

因此,当您提供 Javascript 文件时,浏览器不知道 JS 代码将要做什么并停止所有 DOM 构建过程,而 Javascript 解释器开始解析和执行 Javascript 代码。

即使您在 body 标签的末尾提供 Javascript,浏览器也会对 HTML 和 CSS 进行上述所有步骤,但渲染除外。它会找出 Script 标签并停止直到 JS 完成。

但是 HTML 为脚本标记提供了两个额外的选项:async 和 defer。

异步 ​​- 意味着在下载时执行代码,并且在下载过程中不阻塞 DOM 构建。

Defer - 表示在代码下载完成且浏览器完成 DOM 构建和渲染过程后执行代码。

答案 8 :(得分:0)

如果您的脚本不包含 DOM 操作并且其他脚本不依赖于此,则 Async 是合适的。 例如:bootstrap cdn,jquery

如果您的脚本包含 DOM 操作并且其他脚本依赖于此,则 Defer 是合适的。

例如:<script src=”createfirst.js”> //let this will create element <script src=”showfirst.js”> //after createfirst create element it will show that.

这样就可以了: 例如: <script defer src=”createfirst.js”> //let this will create element <script defer src=”showfirst.js”> //after createfirst create element it will

这将按顺序执行脚本。

但是如果我做了: 例如: <script async src=”createfirst.js”> //let this will create element <script defer src=”showfirst.js”> //after createfirst create element it will

然后,此代码可能会导致意外结果。 Coz:如果html解析器访问createfirst script。它不会停止DOM创建并开始从src下载代码。一旦src解析/代码下载,它将立即与DOM并行执行。

如果showfirst.js比createfirst.js先执行会怎样。如果createfirst需要很长时间(假设在DOM解析完成后),这可能是可能的。然后,showfirst会立即执行。

答案 9 :(得分:0)

Normal vs Async vs Defer

简而言之,Normal vs Async vs Defer

答案 10 :(得分:-3)

asyncdefer将在HTML解析期间下载该文件。两者都不会中断解析器。

  • 下载后,将执行具有async属性的脚本。完成DOM解析后,将执行具有defer属性的脚本。

  • 加载async的脚本不保证任何订单。加载defer属性的脚本维护它们在DOM上的显示顺序。

当脚本不依赖任何内容时,请使用<script async>。 当脚本依赖使用时。

最佳解决方案是在主体的底部添加。阻止或渲染没有问题。

<强>更新

答案已根据评论予以纠正。