脚本标签-异步和延迟

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

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

关于网站加载速度

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

  2. async选项添加到两个脚本并将它们放在<head>页面的顶部会有任何优势吗?

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

  4. 我假设这会导致HTML4浏览器的延迟,但它会加速HTML5浏览器的页面加载吗?

使用<script defer src=...

  1. 将带有属性defer的两个脚本加载到<head>中是否会与在</body>之前加载脚本产生相同的影响?
  2. 我再次假设这会减慢HTML4浏览器的速度。

使用<script async src=...

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

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

最后,在HTML5更常用之前,我最好保持现状吗?

341204 次浏览

将您的脚本放在</body>之前。在某些情况下,Async可以与位于那里的脚本一起使用(请参阅下面的讨论)。对于位于那里的脚本来说,延迟不会有太大的区别,因为DOM解析工作几乎已经完成了。

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

如果你把脚本放在正文的末尾</body>之前,你的超文本标记语言在旧的浏览器中会显示得更快。所以,为了保持旧浏览器的加载速度,你不想把它们放在其他地方。

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

你可以把脚本放在<head>标签中并将它们设置为defer,脚本的加载将被推迟到DOM被解析之后,这将在支持延迟的新浏览器中获得快速的页面显示,但在旧浏览器中它根本不会帮助你,而且它并不比仅仅把脚本放在</body>之前更快,这在所有浏览器中都可以使用。所以,你可以看到为什么最好把它们放在</body>之前。

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

通常jQuery库不是一个很好的async候选者,因为其他脚本依赖于它,并且您想安装事件处理程序,以便您的页面可以开始响应用户事件,并且您可能需要运行一些基于jQuery的初始化代码来建立页面的初始状态。它可以使用async,但必须将其他脚本编码为在加载jQuery之前不执行。

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(异步),浏览器将继续加载超文本标记语言页面并在浏览器加载和执行脚本的同时呈现它。

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

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

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

来源和更多详细信息:这里

asyncdefer将在超文本标记语言解析期间下载文件。两者都不会中断解析器。

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

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

当脚本不依赖任何东西时使用<script async>。 当脚本依赖时,使用<script defer>

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

这张图片解释了正常的脚本标记、异步和延迟

在此处输入图片描述

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

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

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

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

异步

当您将async属性添加到脚本标记时,将发生以下操作。

<script src="myfile1.js" async></script>
<script src="myfile2.js" async></script>
  1. 发出par al lel请求以获取文件。
  2. 他不停地读着文件,好像它从未被打断过一样。
  3. 在下载文件的那一刻执行独立的vid ual脚本。

延迟

dem i lar to async与一个主要的区别非常相似。这是当浏览器访问带有def属性的脚本时发生的事情。

<script src="myfile1.js" defer></script>
<script src="myfile2.js" defer></script>
  1. 使par al lel请求获取indi vid ual文件。
  2. 他不停地读着文件,好像它从未被打断过一样。
  3. 即使脚本文件已下载,也要完成文档的解析。
  4. 每个脚本都按照它们在文档中出现的顺序执行。

参考:异步和延迟的区别

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

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

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

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

  • data-page espeed-no-deler(在页面加载时在任何其他脚本之前执行)
  • 无(在页面加载时可以执行)
  • 延迟(等待DOM加载,所有延迟都按照文件中的放置顺序)
  • async(似乎等到DOM加载)

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

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

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

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

我认为Jake Archibald在2013年向我们提出了一些见解,这可能会给这个话题带来更多的积极影响:

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

圣杯是让一组脚本立即下载而不阻塞渲染,并按照添加的顺序尽快执行。不幸的是,超文本标记语言讨厌你,不会让你这样做。

(…)

答案实际上在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);
});

这为我们的脚本提供了纯超文本标记语言无法实现的行为组合。通过显式不异步,脚本被添加到执行队列中,与我们在第一个纯超文本标记语言示例中添加的队列相同。然而,通过动态创建,它们是在文档解析之外执行的,因此在下载时渲染不会被阻止(不要将非异步脚本加载与同步XHR混淆,这从来都不是一件好事)。

上面的脚本应该内嵌在页面头部,在不影响渐进式渲染的情况下尽快排队下载脚本,并按照你指定的顺序尽快执行。2.js可以在1.js之前免费下载,但直到1.js要么成功下载并执行,要么失败后才会执行。万岁!异步下载但有序执行

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

(…)对于上面的示例,浏览器必须解析和执行脚本以发现要下载的脚本。这会隐藏您的脚本以防预加载扫描器。浏览器使用这些扫描器来发现您可能下次访问的页面上的资源,或者在解析器被其他资源阻止时发现页资源。

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

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

这会告诉浏览器页面需要1.js2.js.link[rel=sub资源]类似于link[rel=preget ch],但具有不同的语义学。不幸的是,它目前仅在Chrome中受支持,并且您必须声明要加载哪些脚本两次,一次通过链接元素,然后在脚本中再次加载。

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

渲染引擎会执行几个步骤,直到它在屏幕上绘制任何内容。

它看起来像这样:

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

CSS渲染引擎为CSS创建不同/分离的数据结构,但它被称为CSSOM(CSS对象模型)

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

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

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

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

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

即使你在body标签的末尾提供Javascript,浏览器也会继续执行上述所有步骤到超文本标记语言和CSS,但渲染除外。

但是超文本标记语言为脚本标记提供了两个额外的选项:异步和延迟。

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

延迟-意味着在下载和浏览器完成DOM构建和渲染过程后执行代码。

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

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

例如:<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

然后,此代码可能会导致意外的结果。 它不会停止DOM创建,并开始从src下载代码。一旦src得到解决/代码下载完毕,它将立即与DOM并行执行。

如果showfirst.js先执行createfirst.js.如果createfirst需要很长时间(假设在DOM解析完成后),则可能会出现这种情况。

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

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

js:保存脚本相关文件。

css:保存样式相关文件。

img:保留图像/图标相关文件

字体:保存字体相关文件


何时使用deer和async属性

deer属性:首先它会下载脚本文件,然后等待html解析。html解析结束后,脚本将执行。换句话说,它将保证所有脚本在html解析后执行。

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

async属性:它将下载脚本文件并在html解析结束时立即执行。换句话说,它不会保证所有脚本都将在html解析后执行。

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

在此处输入图片描述


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