【问题标题】:Script Tag - async & defer脚本标签 - 异步和延迟
【发布时间】:2012-06-04 04:56:04
【问题描述】:

我有几个关于<script> 标签的属性asyncdefer 的问题,据我了解,这些属性仅适用于HTML5 浏览器。

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

关于网站加载速度

  1. 在页面底部的两个脚本中添加async 有什么好处吗?

  2. async 选项添加到两个脚本并将它们放在<head> 的页面顶部会有什么好处吗?

  3. 这是否意味着他们会在页面加载时下载?

  4. 我认为这会导致 HTML4 浏览器出现延迟,但它会加快 HTML5 浏览器的页面加载速度吗?

使用<script defer src=...

  1. 是否会在 <head> 中加载带有属性 defer 的两个脚本与在 </body> 之前加载脚本的效果相同?
  2. 我再次假设这会降低 HTML4 浏览器的速度。

使用<script async src=...

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

  1. 他们会同时下载吗?
  2. 还是一次一个与页面的其余部分一起?
  3. 那么脚本的顺序会成为问题吗?例如,一个脚本依赖于另一个脚本,因此如果一个脚本下载速度更快,则第二个脚本可能无法正确执行,等等。

最后,在 HTML5 更普遍使用之前,我最好保持原样吗?

【问题讨论】:

  • async 是新的(ish),但 defer 自 IE4 以来一直是 IE 的一部分。 defer 最近被添加到其他浏览器中,但这些浏览器的旧版本往往少得多。
  • 现在,HTML5已经非常流行了!
  • defer 与将脚本放置在 HTML 的 底部 相同,这种做法多年来很常见。
  • @vsync 不一定是真的,浏览器在解析 script 标签时会下载带有 defer 标签的 JS,但会推迟到 DOMContentLoaded 之前执行。下载是非阻塞的。放置在 HTML 的底部会延迟 JS 的下载和执行,直到 DOM 构建完成,但您仍然会因等待下载而产生额外的延迟。
  • @vsync 我只是在争论延迟与将脚本放在 HTML 底部不同。设置为 defer 时,浏览器会在后台下载 JS,同时继续构建 DOM。一旦构建了 DOM(触发了 DOMContendLoaded),浏览器将执行已下载的 JS。这就是我所说的“非阻塞”,例如它不会阻止浏览器构建 DOM。这就是为什么它被明确标记为延迟。下面答案中的图片很好地解释了它。

标签: jquery javascript html


【解决方案1】:

这张图片解释了普通的脚本标签,异步和延迟

  • 异步脚本在脚本加载后立即执行,因此它 不保证执行顺序(您包含在 end 可能在第一个脚本文件之前执行)

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

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

【讨论】:

  • 我认为一个包含多个脚本的示例会更好地说明它们的顺序
  • @writofmandamus 看起来async 会赢。见stackoverflow.com/questions/13821151/…
  • 感谢您的精彩解释。但是,图像不是按比例绘制的。在只有<script>标签的情况下,页面加载的总长度会比下载脚本文件所需的时间更长。
  • 如果您不输入asyncdefer,它仍会按该顺序加载。
  • 这真的帮助我更好地理解了。
【解决方案2】:

将您的脚本放在</body> 之前。在少数情况下,异步可以与位于那里的脚本一起使用(参见下面的讨论)。 Defer 不会对位于那里的脚本产生太大影响,因为 DOM 解析工作已经完成了。

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

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

如果您的第二个脚本依赖于第一个脚本(例如,您的第二个脚本使用第一个脚本中加载的 jQuery),那么您不能在没有额外代码来控制执行顺序的情况下使它们异步,但您可以使它们延迟,因为defer 脚本仍然会按顺序执行,只是在文档被解析之后才会执行。如果您有该代码并且不需要立即运行脚本,则可以使它们异步或延迟。

您可以将脚本放在<head> 标记中并将它们设置为defer,脚本的加载将被延迟,直到 DOM 被解析,这将在支持延迟的新浏览器中快速显示页面,但它在较旧的浏览器中对您完全没有帮助,而且它实际上并不比将脚本放在适用于所有浏览器的 </body> 之前更快。因此,您可以了解为什么最好将它们放在 </body> 之前。

当您真的不关心脚本何时加载并且没有其他用户依赖于该脚本加载时,异步会更有用。使用 async 最常被引用的示例是像 Google Analytics 这样的分析脚本,您不希望等待任何东西,并且不急于很快运行,它是独立的,所以没有其他东西依赖它。

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

【讨论】:

  • Defer 应该仍然按顺序运行它们,但在 dom-contentloaded 之前运行。这是否意味着将其放入 head 会更快,因为它可以在解析正文 html 之前开始下载它们?
  • 您说将脚本放在head 并将它们设置为defer 不会比将它们放在</body> 之前快,但据我所知,这是不正确的。想想看——如果你把脚本放在<head>,那么它们会立即开始下载,而如果它们就在</body>之前,那么所有其他元素都会先下载。
  • @Nate - 它不会使您的文档加载速度更快,这是我的观点。您是对的,它可以更快地加载脚本,但它也可能会减慢文档及其内容的加载速度,因为您正在使用一些带宽并使用浏览器将与给定服务器建立的有限连接之一加载脚本,同时它也在尝试加载您的内容。
  • “如果你的第二个脚本依赖于第一个脚本......那么你不能让它们异步或延迟”——这不是真的,延迟它们按顺序执行。
  • 在这一点上,自 2012 年发布此答案以来,对于浏览器开发来说,
【解决方案3】:

HTML5:asyncdefer

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

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

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

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

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

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

【讨论】:

  • blogger.com 模板需要 async="" 才能验证和保存模板更改。
  • 注意:不能保证脚本会按照使用异步指定的顺序运行。 “因此,如果您的第二个脚本依赖于第一个脚本,请避免异步。”
  • async - 脚本在它们被下载的时刻执行,不考虑它们在 HTML 文件中的顺序。
  • 没错,我相信是这样的。
【解决方案4】:

asyncdefer 脚本都立即开始下载,无需暂停解析器,并且都支持可选的 onload 处理程序来解决执行依赖于脚本的初始化的常见需求。

asyncdefer 之间的区别集中在脚本执行的时间上。每个async 脚​​本在完成下载后和窗口加载事件之前第一次执行。这意味着async 脚本有可能(并且很可能)没有按照它们在页面中出现的顺序执行。而另一方面,defer 脚本保证按照它们在页面中出现的顺序执行。该执行在解析完全完成后开始,但在文档的DOMContentLoaded 事件之前。

来源和更多详情:here

【讨论】:

    【解决方案5】:

    面临同样的问题,现在清楚地了解两者的工作原理。希望这个参考链接会有所帮助......

    异步

    当您将 async 属性添加到您的脚本标签时,会发生以下情况。

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

    推迟

    Defer 与 async 非常相似,但有一个主要区别。以下是浏览器遇到具有 defer 属性的脚本时会发生的情况。

    <script src="myfile1.js" defer></script>
    <script src="myfile2.js" defer></script>
    
    1. 发出并行请求以获取各个文件。
    2. 继续解析文档,就好像它从未中断过一样。
    3. 即使脚本文件已下载,也完成对文档的解析。
    4. 按照在文档中遇到的顺序执行每个脚本。

    参考:Difference between Async and Defer

    【讨论】:

      【解决方案6】:

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

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

      • 使用async 加载的脚本不保证任何顺序。虽然使用defer 属性加载的脚本会保持它们在 DOM 上出现的顺序。

      当脚本不依赖任何东西时使用&lt;script async&gt;。 当脚本依赖时使用&lt;script defer&gt;

      最好的解决方案是在正文底部添加&lt;script&gt;。阻塞或渲染不会有问题。

      【讨论】:

      • 这里只是想澄清一下,这里发生了两件事 1. 资源的下载 2. 资源的执行。在这两种情况下(异步和延迟)的资源下载都不会阻塞,这意味着它们不会阻塞 html 的解析,而异步执行会阻塞解析,并且在延迟的情况下,会在 html 标记被解析后执行,因此在这种情况下是非阻塞的。
      • @pOoOf 由于请求和下载资源是通过h​​ttp请求线程完成的,它与GUI渲染线程并行执行,不会阻塞html的解析,对吧?但是js引擎线程和GUI render线程是互斥的,所以当js引擎线程执行js代码时,解析html的GUI渲染线程会被阻塞。
      【解决方案7】:

      我认为 Jake Archibald 早在 2013 年就向我们展示了一些见解,这些见解可能会为该主题增添更多积极性:

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

      圣杯是在不阻塞渲染的情况下立即下载一组脚本,并按照它们添加的顺序尽快执行。不幸的是,HTML 讨厌你,不会让你这样做。

      (...)

      答案实际上在 HTML5 规范中,尽管它隐藏在脚本加载部分的底部。 "async IDL 属性控制元素是否异步执行。如果设置了元素的 "force-async" 标志,那么在获取时,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 示例中添加的队列相同。但是,通过动态创建,它们在文档解析之外执行,因此在下载它们时不会阻止渲染(不要将非异步脚本加载与同步 XHR 混淆,这绝不是一件好事)。

      上述脚本应内嵌在页面头部,在不中断渐进式渲染的情况下尽快排队下载脚本,并按您指定的顺序尽快执行。 “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 中受支持,您必须声明要加载哪些脚本两次,一次通过链接元素,另一次在您的脚本中。

      更正:我最初说这些是由预加载扫描器拾取的,它们不是,它们是由常规解析器拾取的。然而,预加载扫描器可以拾取这些,但它还没有,而可执行代码包含的脚本永远不能被预加载。感谢在 cmets 中纠正我的 Yoav Weiss。

      【讨论】:

        【解决方案8】:

        良好的做法是将所有文件保留在源文件夹中以快速加载源文件。您需要下载所有与脚本、样式、图标和图像相关的文件,并将这些文件放入您的项目文件夹中。

        在您的项目中创建这些文件夹以保存不同的源文件,然后将所需文件从这些文件夹添加到页面中。

        js: 保存脚本相关文件。

        css: 保存与样式相关的文件。

        img:保存图片/图标相关文件

        字体:保存字体相关文件


        何时使用 defer 和 async 属性

        defer属性:首先会下载脚本文件,然后等待html解析。 html解析结束后,将执行脚本。也就是说,它会保证所有的脚本在html解析后都会执行。

        当脚本用于 DOM 操作时,Defer 属性很有用。意味着脚本将应用于文档 html。

        async 属性: 它会下载脚本文件并执行,无需等待 html 解析结束。也就是说,它不能保证所有的脚本都会在html解析后执行。

        当脚本不用于 DOM 操作时,异步属性很有用。有时您只需要脚本用于服务器端操作或处理缓存或 cookie,而不需要用于 DOM 操作。表示脚本与使用的 html 无关。


        何时使用 defer 和 async 的有用链接: https://stackoverflow.com/a/68929270/7186739

        【讨论】:

          【解决方案9】:

          似乎 defer 和 async 的行为取决于浏览器,至少在执行阶段是这样。注意, defer 仅适用于外部脚本。我假设 async 遵循相同的模式。

          在 IE 11 及以下版本中,顺序似乎是这样的:

          • 异步(页面加载时可能部分执行)
          • 无(可以在页面加载时执行)
          • defer(页面加载后执行,全部按文件放置顺序延迟)

          在 Edge、Webkit 等中,async 属性似乎被忽略或放在末尾:

          • data-pagespeed-no-defer(在任何其他脚本之前执行,同时页面正在加载)
          • 无(可以在页面加载时执行)
          • defer(等到 DOM 加载完毕,全部按文件中的放置顺序延迟)
          • 异步(似乎要等到 DOM 加载完毕)

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

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

          旁白:加载时外部 javascript 的大小确实很重要……但对执行顺序没有影响。

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

          【讨论】:

          【解决方案10】:

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

          看起来像这样:

          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 为 script 标签提供了两个额外的选项:async 和 defer。

          异步 ​​- 表示下载时执行代码,在下载过程中不阻塞 DOM 构建。

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

          【讨论】:

            【解决方案11】:

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

            如果您的脚本包含 DOM 操作并且其他脚本依赖于此,则延迟适用。

            例如:&lt;script src=”createfirst.js”&gt; //let this will create element &lt;script src=”showfirst.js”&gt; //after createfirst create element it will show that.

            这样就可以了: 如: &lt;script defer src=”createfirst.js”&gt; //let this will create element &lt;script defer src=”showfirst.js”&gt;//after createfirst create element it will

            这将按顺序执行脚本。

            但如果我做了: 例如: &lt;script async src=”createfirst.js”&gt; //let this will create element &lt;script defer src=”showfirst.js”&gt;//after createfirst create element it will

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

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

            【讨论】:

              【解决方案12】:

              简而言之,普通 vs 异步 vs 延迟

              如果你没有在脚本标签上指定任何东西,它的正常流程和那些数字 1,2,3,4 代表它如何执行的步骤

              【讨论】:

              • 似乎是一个未完成的答案@suseendhar-lal
              猜你喜欢
              • 2015-04-17
              • 1970-01-01
              • 2016-10-03
              • 1970-01-01
              • 2018-03-25
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多