推迟发表地址MDN says:
此布尔属性设置为向浏览器指示脚本将在解析文档后执行,但在触发DOMContentLoaded 之前执行。 defer属性应仅用于外部脚本。
在DOMContentLoaded
MDN also says上:
当初始HTML文档已完全加载并解析时,会触发DOMContentLoaded事件,而不等待样式表 ......
所以DOMContentLoaded
在CSSOM
准备好之前被解雇了。这意味着在CSSOM
准备就绪之前执行延迟脚本。但如果这是真的,那些scrips必须无法获得正确的css属性值,并且不能正确应用css。但事实并非如此,我们知道所有延迟脚本都运行良好。
P.S:请不要google says在执行任何内联javscript之前构建CSSOM
但谷歌在技术上是不正确的。在CSSOM准备好之前,内联JavaScript会被执行。从我的测试中我发现MDN是正确的,如果在css文件(或js是内联)之前下载了js文件(延迟和非延迟),那么在CSSOM准备好之前执行js。所以js可能会错误地处理样式。为了避免这种情况,我们需要在所有js逻辑之前进行强制回流。
因此,如果用户访问我们的网站时所有需要缓存的js已经缓存且css没有缓存,或者在css之前下载了js,那么他可能会看到错误渲染的页面。为避免这种情况,我们应在所有网站上添加强制回流功能。 js文件。
答案 0 :(得分:5)
DOMContentLoaded
可以在CSSOM source
解析HTML后不久会触发domContentLoaded事件;浏览器知道不会阻止JavaScript,因为没有其他解析器阻塞脚本,CSSOM结构也可以并行进行。
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>
答案 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事件(具有红色角落的事件)。
答案 2 :(得分:2)
虽然我没有真正阅读规范。以下内容基于Chrome的实际行为(在Chromium 68,Ubuntu上观察到)。如果行为只是规范中未定义,则行为可能因浏览器而异。例如在2010年scripts don't always wait for proceeding stylesheets。我假设已达成协议,并且多年来对行为进行了标准化。
defer
脚本在domInteractive
之后,domContentLoaded
之前执行;这是顺序的。 domInteractive
和domContentLoaded
是两个时间戳,可以在Chrome devtools的效果(以前称为时间轴)标签中查看。可能也在其他类似工具中,但我没有尝试过。
domInteractive
是HTML解析和初始DOM构造完成(并且所有“同步”脚本已完成执行)的关键点。 document.readyState
从'loading'
变为'interactive'
; readystatechange
会相应触发document
事件。
所有defer
脚本均按其出现顺序执行。然后是domContentLoaded
,DOMContentLoaded
上将触发document
事件。
每个同步脚本,无论是内部还是外部的,都等待解析之前的样式表(当然是在提取之后)。
是的,后续样式表不会阻止同步脚本。 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
(also in WHATWG's spec)
async
脚本对执行顺序没有承诺;每个async
脚本一被获取,就会被“排队执行”;一旦渲染过程空闲,它们便被执行。 (确切地说,不同类型的资源具有不同的优先级。The spec提供了宝贵的要求)这些应该很好地解释hinok's two examples,前者async
(来自Google)和后者defer
。
我在页面加载时没有使用CSSOM的经验(尽管我在页面加载时确实在DOM上操作),所以我无法提供可靠的建议。似乎“ load
上的window
事件”或“提前强制回流”可能有效。