我有几个关于<script>标签的async和defer属性的问题,据我的理解,它只在HTML5浏览器中工作。

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

在网站加载速度方面

在我在页面底部的两个脚本中添加async是否有任何优势? 在这两个脚本中添加async选项并将它们放在<head>的页面顶部是否有任何优势? 这是否意味着他们会在页面加载时下载? 我认为这会导致HTML4浏览器的延迟,但是它会加速HTML5浏览器的页面加载吗?

使用<script defer src=…

在<head>中加载带有属性的两个脚本是否与在</body>之前加载脚本具有相同的影响? 同样,我认为这会降低HTML4浏览器的速度。

使用<script async src=…

如果我有两个脚本异步启用

他们会同时下载吗? 还是一页一页地看? 脚本的顺序会成为问题吗?例如,一个脚本依赖于另一个脚本,所以如果一个脚本下载得更快,第二个脚本可能无法正确执行等等。

最后,在HTML5得到更广泛的应用之前,我是否应该保持现状?


当前回答

看起来defer和async的行为是依赖于浏览器的,至少是在执行阶段。说明“defer”仅适用于外部脚本。我假设async遵循相同的模式。

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

异步(可以在页面加载时部分执行) 无(可以在页面加载时执行) 延迟(在页面加载后执行,所有延迟在文件中的位置顺序)

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

data-page - speed-no-defer(在加载页面时,在任何其他脚本之前执行) 无(可以在页面加载时执行) 延迟(等待DOM加载,所有延迟在文件中的位置顺序) async(似乎等待DOM加载)

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

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

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

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

其他回答

我认为杰克·阿奇博尔德在2013年向我们展示了一些见解,可能会为这个话题增添更多的积极因素:

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

The holy grail is having a set of scripts download immediately without blocking rendering and execute as soon as possible in the order they were added. Unfortunately HTML hates you and won’t let you do that. (...) The answer is actually in the HTML5 spec, although it’s hidden away at the bottom of the script-loading section. "The async IDL attribute controls whether the element will execute asynchronously or not. If the element's "force-async" flag is set, then, on getting, the async IDL attribute must return true, and on setting, the "force-async" flag must first be unset…". (...) Scripts that are dynamically created and added to the document are async by default, they don’t block rendering and execute as soon as they download, meaning they could come out in the wrong order. However, we can explicitly mark them as not 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);
});

This gives our scripts a mix of behaviour that can’t be achieved with plain HTML. By being explicitly not async, scripts are added to an execution queue, the same queue they’re added to in our first plain-HTML example. However, by being dynamically created, they’re executed outside of document parsing, so rendering isn’t blocked while they’re downloaded (don’t confuse not-async script loading with sync XHR, which is never a good thing). The script above should be included inline in the head of pages, queueing script downloads as soon as possible without disrupting progressive rendering, and executes as soon as possible in the order you specified. “2.js” is free to download before “1.js”, but it won’t be executed until “1.js” has either successfully downloaded and executed, or fails to do either. Hurrah! async-download but ordered-execution!

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

(…)对于上面的示例,浏览器必须解析和执行脚本以发现要下载哪些脚本。这将从预加载扫描器中隐藏脚本。浏览器使用这些扫描器来发现您接下来可能访问的页面上的资源,或者在解析器被其他资源阻塞时发现页面资源。 我们可以通过在文档头部添加以下内容来增加可发现性:

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

This tells the browser the page needs 1.js and 2.js. link[rel=subresource] is similar to link[rel=prefetch], but with different semantics. Unfortunately it’s currently only supported in Chrome, and you have to declare which scripts to load twice, once via link elements, and again in your script. Correction: I originally stated these were picked up by the preload scanner, they're not, they're picked up by the regular parser. However, preload scanner could pick these up, it just doesn't yet, whereas scripts included by executable code can never be preloaded. Thanks to Yoav Weiss who corrected me in the comments.

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

如果您的脚本包含DOM操作,而其他脚本依赖于此,那么Defer是合适的。

例如:<script src= " createfirst.js " > //让这将创建元素<script src= " showfirst.js " > //在createfirst创建元素后,它将显示。

这样就可以: 例如:<script defer src= " createfirst.js " > //let this will create element <script defer src= " showfirst.js " > //在createfirst.js创建元素后,它将

这将按顺序执行脚本。

但如果我做出: 例如:<script async src= " createfirst.js " > //let this will create element <script defer src= " showfirst.js " > //在createfirst.js创建元素后,它将

然后,这段代码可能会产生意想不到的结果。 因为:如果html解析器访问createfirst脚本。它不会停止创建DOM,而是开始从src下载代码。一旦src被解析/代码被下载,它将立即与DOM并行执行。

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

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

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

Js:保存脚本相关文件。

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

Img:保存图像/图标相关文件

字体:保存字体相关文件


何时使用defer和async属性

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

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

async属性:它将下载脚本文件并执行,而不等待html解析结束。换句话说,它不能保证在html解析之后执行所有脚本。

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


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

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

异步

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

<script src="myfile1.js" async></script>
<script src="myfile2.js" async></script>

发出并行请求来获取文件。 继续解析文档,就像它从未被打断过一样。 文件下载后立即执行各个脚本。

推迟

延迟与异步非常相似,但有一个主要区别。下面是浏览器遇到带有defer属性的脚本时会发生的情况。

<script src="myfile1.js" defer></script>
<script src="myfile2.js" defer></script>

进行并行请求以获取单个视频文件。 继续解析文档,就像它从未被打断过一样。 即使已经下载了脚本文件,也要完成文档解析。 按照文档中出现的顺序执行每个脚本。

参考:异步和延迟的区别

看起来defer和async的行为是依赖于浏览器的,至少是在执行阶段。说明“defer”仅适用于外部脚本。我假设async遵循相同的模式。

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

异步(可以在页面加载时部分执行) 无(可以在页面加载时执行) 延迟(在页面加载后执行,所有延迟在文件中的位置顺序)

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

data-page - speed-no-defer(在加载页面时,在任何其他脚本之前执行) 无(可以在页面加载时执行) 延迟(等待DOM加载,所有延迟在文件中的位置顺序) async(似乎等待DOM加载)

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

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

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

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