延迟脚本是否在DOMContentLoaded事件之前执行?

use*_*782 15 html javascript css dom cssom

在推迟attirbute时,MDN说:

此布尔属性设置为向浏览器指示在解析文档之后但在触发DOMContentLoaded之前要执行脚本.defer属性应仅用于外部脚本.

DOMContentLoaded MDN上也说:

完全加载和解析初始HTML文档时会触发DOMContentLoaded事件,而无需等待样式表 ...

所以DOMContentLoadedCSSOM准备好之前就被解雇了.这意味着延迟脚本在准备好之前执行CSSOM.但如果这是真的,那些scrips必须无法获得正确的css属性值,并且不能正确应用css.但事实并非如此,我们知道所有延迟脚本都能正常运行.

  1. 那么MDN文档在技术上是不正确的吗?
  2. 我在哪里可以找到DOMContentLoaded`的官方文档?我在https://dom.spec.whatwg.org/搜索但找不到它.

PS:请不要谷歌说在执行任何内联javscript之前构建CSSOM

在此输入图像描述

但谷歌在技术上是不正确的.在CSSOM准备好之前,内联JavaScript会被执行.从我的测试中我发现MDN是正确的,如果在css文件(或js是内联)之前下载了js文件(延迟和非延迟),那么在CSSOM准备好之前执行js.所以js可能会错误地处理样式.为了避免这种情况,我们需要在所有js逻辑之前进行强制回流.

因此,如果用户访问我们的网站时所有需要缓存的js和css没有缓存,或者在css之前下载了js,那么他可能会看到错误渲染的页面.为了避免这种情况,我们应该在所有网站的js文件中添加强制重排.

Tim*_*zio 10

我使用延迟脚本加载。某位知名网站性能专家提供了冗长的技术解释。他清楚地指出延迟是要走的路(出于这个和那个技术原因,有各种数据和图表支持,许多人似乎认为这是公开辩论的,re: async)。

所以我开始使用它。延迟脚本的优点是下载异步,但按显示的顺序执行,这可能是异步的问题(例如,您可以在供应商包之前加载您的应用程序包,因为您无法控制异步脚本的执行顺序,只需说“按此顺序”)。

然而,我马上发现,虽然这解决了这个问题,但这可能意味着,根据你获取包的方式,CSS 包没有加载。因此,您最终可能会得到无样式的内容,具体取决于您的设置方式。请注意,对于 defer,他们还说您不应该在这些脚本中写入 dom 等(这在您的文档方面再次有意义)。

所以看起来你的文档是正确的。效果很容易重现。

我如何摆脱它;最基本的方法,是这样的:

<script src="css.bundle.js"></script>
<script src="vendor.bundle.js" defer></script>
<script src="angular.bundle.js" defer></script>
<script src="app.bundle.js" defer></script>
Run Code Online (Sandbox Code Playgroud)

这可以确保首先加载 css,这样您的主页等就会很好地显示出来,并且还确保(尽管所有三个都在异步加载)app.bundle 将最后执行,确保所有其他依赖项都井井有条.

因此,您只需使用最低限度的 CSS 即可启动应用程序,将其创建为一个包,然后首先加载它,然后再进行任何操作。否则,您可以为每个模块/组件捆绑 CSS,依此类推。

这个主题还有很多,我可能会做更多,但同样(我会尝试找到参考),这是性能向导公然推荐的,所以我尝试了它,它对我来说似乎非常有效。

编辑:令人着迷,在寻找那个参考文献(我还没有找到)时,我浏览了一些关于这个主题的“专家”。建议大相径庭。有人说 async 在所有方面都要优越得多,有人说 defer。陪审团似乎对这个话题很感兴趣,总的来说,我认为这可能更多地与你如何构建你的脚本有关,而不是一个脚本是否真的比另一个更好。

再次编辑:这里有更多证据。我使用上述简单的加载顺序在存根网站上运行了一个性能分析器,故意使脚本简单化,以便它们在时间线中可见。

这是结果的 SS:这里有四个黄色框。前三个是对剧本的评价。第四个(当您在工具中将鼠标悬停在它上面时,这只是 SS 记住的)是 DOMContentLoaded 事件(带有红色角落的那个)。

在 DOMContentLoaded 事件之前加载/评估脚本


小智 7

不过,我并没有真正阅读规范。以下内容基于Chrome 的实际行为(在 Chromium 68、Ubuntu 上观察)。如果只是在规范中未定义,浏览器之间的行为可能会有所不同。例如,2010 年的脚本并不总是等待处理样式表。我假设多年来已经达成了协议并且行为已经标准化。


defer脚本执行后domInteractive,前domContentLoaded; 它是连续的。

domInteractivedomContentLoaded是两个时间戳,可以在 Chrome devtools 的 Performance(以前的 Timeline)选项卡中查看。可能也在其他类似的工具中,但我还没有尝试过。

domInteractive是 HTML 解析和初始 DOM 构建完成(并且所有“同步”脚本已完成执行)的时刻。document.readyState'loading'变为'interactive'; 一个readystatechange事件相应地触发document

所有defer脚本都按其出现的顺序执行。然后来了domContentLoaded,一个DOMContentLoaded事件触发document

DOM 和 CSSOM 构建不相互依赖;但同步脚本可能会引入依赖项。

每个同步脚本,内部或外部,等待前面的样式表被解析(当然,在获取之后)。

是的,同步脚本不会被后续样式表阻止。MDN和Google等文章说“脚本依赖CSSOM准备好”;他们(可能)没有提到只依赖前面的部分。

PS:请不要说 google 在执行任何内联 javscript 之前说 CSSOM 是构建的

谷歌没有这么说(至少,在我阅读这篇文章的时候)。

相反,在获取一个同步脚本(如果是外部的)并执行之前,它之后的任何代码、HTML、样式表或其他脚本都无法解析/执行/构建。他们阻止了他们之后的任何事情。

因此,在特定情况下,例如。如果没有同步脚本,DOMContentLoaded事件可能会CSSOM 准备好之前或之后触发。这就是 MDN 所说的“无需等待样式表”的意思。

defer/async脚本根本不关心样式表。

与同步脚本不同,defer/async脚本不等待前面的样式表,也不阻塞后续的样式表/脚本。它们完全从那些“依赖链”中移除。您不能依赖任何已解析的处理样式表。

defer/之间的区别async

  • 如上所述,defer脚本具有可预测的执行时间;DOM 已经准备好了。他们还承诺按顺序执行。

    更新: defer脚本被添加到列表的末尾W3C 的规范(第 20 项) W3C 的规范说,defer 脚本被添加到列表的末尾
    也在 WHATWG 的规范中

  • async脚本对执行顺序没有承诺;每个async脚本一旦被获取就会“排队执行”;一旦渲染进程空闲,它们就会被执行。(确切地说,不同类型的资源有不同的优先级。规范提供了宝贵的要求)

这些应该很好地解释了hinok 的两个例子,前者async(来自谷歌)和后者defer


我在页面加载时使用 CSSOM 的经验不多(不过,我确实在页面加载时对 DOM 进行操作),因此我无法提供可靠的建议。似乎“load事件发生window”或“尽早强制回流”可能有效。


hin*_*nok 5

DOMContentLoaded可以在CSSOM之前触发,来源

解析HTML之后不久,就会触发domContentLoaded事件。浏览器知道不会阻止JavaScript,并且由于没有其他解析器阻止脚本,因此CSSOM构造也可以并行进行。

在此处输入图片说明

文章在谷歌开发者介绍async,而不是defer,但你的问题的情况下,因为基于它不会改变任何东西史蒂夫Sourders文章perfplanet

DEFER脚本在DOM Interactive之后执行。

在文章下的评论

[...]规范说DEFER脚本在之后domInteractive但之前运行domContentLoaded

您可以进行自己的实验,在下面查找使用defer和时间轴的代码

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.3/angular-material.css">
</head>
<body>
  <h1>App</h1>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/js/bootstrap.js" defer></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明