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

时间:2017-03-22 11:38:31

标签: javascript html css dom cssom

推迟发表地址MDN says

  

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

DOMContentLoaded MDN also says上:

  

当初始HTML文档已完全加载并解析时,会触发DOMContentLoaded事件,而不等待样式表 ......

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

  1. MDN文档在技术上是不正确的吗?
  2. 我在哪里可以找到DOMContentLoaded`的官方文档?我在https://dom.spec.whatwg.org/搜索但无法找到它。
  3. P.S:请不要google says在执行任何内联javscript之前构建CSSOM

    enter image description here

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

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

3 个答案:

答案 0 :(得分:5)

DOMContentLoaded可以在CSSOM source

之前触发
  

解析HTML后不久会触发domContentLoaded事件;浏览器知道不会阻止JavaScript,因为没有其他解析器阻塞脚本,CSSOM结构也可以并行进行。

enter image description here

Google Developer上的文章描述的是async而不是defer,但就您的问题而言,它不会改变任何内容,因为Steve Sourders article基于perfplanet

  

DEFER脚本在DOM Interactive之后执行。

his comment在他的文章

  

[...]规范说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>

enter image description here

答案 1 :(得分:4)

我使用延迟脚本加载。有些人是一位知名的网站性能大师,有一个冗长的技术解释。他明确指出延迟是要走的路(为了这个和那个技术原因,在各种数据和图表的支持下,许多人似乎觉得这个问题是开放式的辩论,重新:异步)。

所以我开始使用它。延迟脚本具有下载异步的优势,但是按顺序执行,这可能是异步的问题(例如,您可以在供应商捆绑之前加载您的应用程序包,因为您不能控制异步脚本的执行顺序按顺序说&#34;&#34;)。

然而,我立刻发现虽然这解决了这个问题,但这可能意味着,取决于你如何抓取你的捆绑包,CSS捆绑包没有加载。因此,根据您的设置方式,您最终可以获得无格式的内容。请注意,对于延迟,他们还说您不应该在这些脚本中写入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>

这确保了css首先加载,所以你的主页等会很好地显示,并且还确保(虽然所有三个都加载异步),app.bundle将最后执行,确保所有其他依赖项是有序的。

因此,您可以使用所需的绝对最低限度的CSS来启动应用程序,将其创建为捆绑包,然后在任何情况下先加载它。否则,您可以在每个模块/组件的CSS中捆绑,依此类推。

这个话题还有很多,我可能会做得更多,但我会再次尝试(我会尝试找到参考),这是性能向导公开推荐的,所以我试了一下,看起来很漂亮对我有效。

编辑:引人入胜,在寻找那个参考资料时(我还没有找到),我经历了一些&#34;专家&#34;就此主题而言。建议差别很大。有人说async在所有方面都要优越得多,有人说推迟。陪审团似乎真的出现在这个话题上,总的来说,我说它可能与你构建脚本的方式有关,而不是一个人实际上比另一个更好。

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

这里是结果的SS:这里有四个黄色方框。前三个是脚本的评估。第四个(当你在工具中鼠标悬停它时,这只是SS记住)是DOMContentLoaded事件(具有红色角落的事件)。

Scripts loading/evaluating before DOMContentLoaded event

答案 2 :(得分:2)

虽然我没有真正阅读规范。以下内容基于Chrome的实际行为(在Chromium 68,Ubuntu上观察到)。如果行为只是规范中未定义,则行为可能因浏览器而异。例如在2010年scripts don't always wait for proceeding stylesheets。我假设已达成协议,并且多年来对行为进行了标准化。


defer脚本在domInteractive之后,domContentLoaded之前执行;这是顺序的。

domInteractivedomContentLoaded是两个时间戳,可以在Chrome devtools的效果(以前称为时间轴)标签中查看。可能也在其他类似工具中,但我没有尝试过。

domInteractive是HTML解析和初始DOM构造完成(并且所有“同步”脚本已完成执行)的关键点。 document.readyState'loading'变为'interactive'readystatechange会相应触发document事件。

所有defer脚本均按其出现顺序执行。然后是domContentLoadedDOMContentLoaded上将触发document事件。

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

每个同步脚本,无论是内部还是外部的,都等待解析之前的样式表(当然是在提取之后)。

是的,后续样式表不会阻止同步脚本。 MDN和Google以及其他文章说“脚本取决于CSSOM是否准备就绪”;他们(可能)没有提到仅依赖于先前的部分。

  

P.S:请不要让Google说在执行任何内嵌式Javascript之前先构建CSSOM

Google并未这样说(至少在我阅读本文时)。

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

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

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

与同步脚本不同,defer / async脚本不会等待前面的样式表,也不会阻止后面的样式表/脚本。它们完全从那些“依赖链”中删除。您不能依靠任何进行中的样式表进行解析。

defer / async之间的区别:

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

    更新:   W3C's spec (the 20th item)说,defer脚本被添加到列表的 end   defer scripts are added to the end of the list, said W3C's spec
      (also in WHATWG's spec

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

这些应该很好地解释hinok's two examples,前者async(来自Google)和后者defer


我在页面加载时没有使用CSSOM的经验(尽管我在页面加载时确实在DOM上操作),所以我无法提供可靠的建议。似乎“ load上的window事件”或“提前强制回流”可能有效。