36

根据延迟属性MDN 说

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

DOMContentLoaded MDN 上还说

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

所以在准备好DOMContentLoaded之前被解雇。CSSOM这意味着延迟脚本在准备好之前执行。CSSOM但如果这是真的,则脚本必须无法获得正确的 CSS 属性值,并且不能正确应用 CSS。但这不是真的,我们知道所有延迟脚本都运行良好。

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

PS:请注意,谷歌说CSSOM 是在执行任何内联 javascript 之前构建的

在此处输入图像描述

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

因此,如果用户访问我们的网站时所有需要的 js 都已缓存并且 CSS 未缓存或 js 在 CSS 之前下载,那么他可能会看到一个错误呈现的页面。为了避免这种情况,我们应该在我们所有网站的 js 文件中添加强制回流。

4

3 回答 3

11

我使用延迟脚本加载。某位著名的网站性能专家给出了冗长的技术解释。他清楚地指出,延迟是要走的路(出于这个和那个技术原因,有各种数据和图表支持,许多人似乎觉得可以广泛讨论,re: async)。

所以我开始使用它。延迟脚本具有下载异步的优势,但按呈现的顺序执行,这可能是异步的问题(例如,您可以在供应商包之前加载应用程序包,因为您不能通过说来控制异步脚本的执行顺序“按此顺序”)。

然而,我马上发现虽然这解决了这个问题,但这可能意味着,取决于你如何获取你的包,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,依此类推。

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

编辑:令人着迷的是,在寻找那个参考资料(我还没有找到)时,我通过了一些关于这个主题的“专家”。这些建议大相径庭。有人说异步在所有方面都优越得多,有人说延迟。陪审团似乎真的对这个话题不感兴趣,总的来说,我想说它可能更多地与你如何构建你的脚本有关,而不是一个实际上是否比另一个更好。

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

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

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

于 2017-03-22T11:49:52.570 回答
9

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


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

domInteractive并且domContentLoaded是两个时间戳,可以在 Chrome devtools 的性能(以前的时间线)选项卡中查看。可能也在其他类似的工具中,但我没有尝试过。

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

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

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

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

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

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

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

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

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

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

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

defer/之间的区别async

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

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

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

这些应该很好解释了hinok的两个例子,前者async(来自Google)和后者defer。</p>


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

于 2018-08-23T08:03:14.353 回答
6

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

domContentLoaded 事件在 HTML 被解析后不久触发;浏览器知道不阻塞 JavaScript,并且由于没有其他解析器阻塞脚本,CSSOM 构建也可以并行进行。

在此处输入图像描述

Google Developer 上的文章描述async而不是defer但就您的问题而言,它不会改变任何东西,因为基于关于perfplanetSteve Sourders 文章

DEFER 脚本在 DOM Interactive 之后执行。

以及他在文章下的评论

[...] 规范说 DEFER 脚本domInteractivedomContentLoaded.

你可以自己做实验,看看下面的代码使用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>

在此处输入图像描述

于 2017-03-22T12:05:18.533 回答