CSS阻止页面解析时该怎么办?

最近,我对一个站点进行了审核,并遇到了preload/polyfill一些客户已经看到的模式今天,不建议使用此先前流行的模式。但是,考虑它对于说明Web浏览器谨慎使用预载材料机制的重要性很有用。这也是有趣的,因为它可以让你展示如何在文档中元素的顺序会影响性能的一个真实的例子(这是在讨论这个美妙的哈里·罗伯茨的文章)。 该材料(我们今天出版的翻译)专门用于分析CSS资源使用不当和不及时处理会影响网页性能的情况。





关于loadCSS


我是Filament Group的忠实拥护者 -他们发布了数量惊人的优秀项目。此外,他们不断创建宝贵的工具并共享它们,以改善Web。这些工具之一就是loadCSS,很长一段时间以来,我一直是我推荐所有人用来加载非关键CSS资源的工具。

尽管现在情况已经改变了(Filament Group发表了一篇很好的文章,介绍了他们的员工最近喜欢使用什么),但是当我审核站点时,我仍然经常看到他们loadCSS在生产中如何使用它。

我遇到的模式之一是模式preload/polyfill使用这种方法,所有具有样式的文件都将以预加载模式加载(rel相应链接的属性设置为preload)。之后,当他们准备好使用时,应用其事件将其onload连接到页面。

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/mystylesheet.css"></noscript>

由于并非所有的浏览器都支持design <a href="https://caniuse.com/#feat=link-rel-preload"><link rel="preload"></a>,因此该项目loadCSS为开发人员提供了一个方便的polyfill,它将在相关链接的描述之后添加到页面中:

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

网络优先顺序混乱


我从不热衷于这种模式。预加载是一种粗鲁的工具。使用属性链接下载的资料可以rel="preload"与其他资料争夺网络资源。使用情况preload假定由于样式表在页面输出中不发挥关键作用而异步加载的样式表会从浏览器获得很高的优先级。

下图取自WebPageTest,很好地演示了此问题。在第3-6行中,您可以看到使用pattern异步加载CSS文件preload但是,尽管开发人员认为这些文件不是很重要,以至于下载它们会阻止渲染,但是请使用preload 表示将在浏览器接收剩余资源之前加载它们。


加载时使用预加载模式的CSS文件将比其他资源更早地进入浏览器,即使它们不是对页面的初始呈现至关重要的资源

HTML解析器锁


与资源加载优先级相关的问题已经足够避免在大多数情况下使用该模式preload但是在这种情况下,由于存在另一个以常规方式加载的样式表,使情况更加恶化。

<link rel="stylesheet" href="path/to/main.css" />
<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

使用时也会出现同样的问题,preload导致最重要的文件没有获得高优先级。但这对浏览器解析页面的能力具有同样重要的作用,也许不太明显。

同样,它已经被详细地,所以我建议阅读该材料,以便更好地了解正在发生的事情。在这里,我将简短地讨论这一点。

通常,加载样式会阻止页面呈现。浏览器需要查询和解析样式才能显示页面。但是,这不会阻止浏览器解析其余的HTML代码。

另一方面,如果脚本未标记为defer或,则会阻止解析器async

由于浏览器必须假定脚本可以操纵页面本身的内容或应用于页面的样式,因此在运行此脚本时需要格外小心。如果浏览器知道正在加载某些CSS代码,它将等待此CSS代码到达,然后再运行脚本。而且由于浏览器在执行脚本之前无法继续解析文档,因此这意味着样式不再只是阻止渲染。它们阻止浏览器解析HTML。

对于外部脚本和页面中嵌入的脚本,此行为都是正确的。如果CSS加载,则在此CSS到达浏览器之前,不会运行内联脚本。

问题研究


可视化此问题的最容易理解的方法是使用Chrome开发人员工具(我真的很喜欢这些工具的发展水平)。

在Chrome工具中,有一个标签Performance可用来记录页面加载配置文件。我建议人为地减慢网络连接速度,以使问题更加明显。

在这种情况下,我使用Fast 3G网络设置进行了测试。如果仔细查看主线程发生了什么,可以理解,加载CSS文件的请求是在HTML解析的最开始时发生的(大约在页面开始加载后1.7秒)。


HTML解析块下面的一个小矩形表示接收CSS文件的请求,

在接下来的大约一秒钟的时间内,主线程处于不活动状态。在这里您可以看到活动的小岛。这是事件的触发,指示样式的加载完成;它是预加载机制发送的其他请求的资源。但是浏览器完全停止了对HTML的解析。


如果您看大图,事实证明,开始加载CSS后,主流处于非活动状态的时间超过1.1秒,

因此,经过2.8秒,加载了样式,浏览器对其进行处理。只有这样,我们才能看到内置脚本的处理过程,然后浏览器最终返回到HTML解析。


CSS大约需要2.8秒到达,此后我们看到浏览器继续解析HTML

Firefox是一个很好的例外


对于Chrome,Edge和Safari,上述行为很常见。Firefox是流行浏览器列表中的一个很好的例外。

所有其他浏览器都暂停HTML解析,但是使用主动解析器(一种预加载材料的方式)来查看代码以获取到外部资源的链接并满足加载这些资源的请求。但是,Firefox在这方面走得更远:即使它希望脚本能够执行,它也可以推测性地构建 DOM树。

除非脚本操作DOM(导致需要丢弃推测分析的结果),否则此方法将允许Firefox加以利用。当然,如果浏览器必须删除以推测方式构造的DOM树,则意味着他通过构建此树没有任何用处。

这是一个有趣的方法。我非常想知道它的有效性。但是,现在,Firefox Performance Profiler中没有有关此信息。在那里,您无法确定推测性解析器是否有效,是否需要重做它所做的工作以及是否仍需要重做,这将如何影响性能。

我与负责Firefox开发人员工具的人员进行了交谈,我可以说他们对于将来如何在探查器中呈现此类信息有有趣的想法。我希望他们能成功。

解决问题


对于我在开始时提到的客户,解决此问题的第一步看起来非常简单:摆脱模式preload/polyfill预加载非关键CSS是没有意义的。在这里,您需要切换为使用而不是rel="preload"attribute media="print"这正是灯丝小组专家的建议。此外,这种方法还使您完全摆脱了polyfill。

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

这已经使我们处于一个更好的位置:现在,网络优先级与下载的真正重要性更加一致。而且我们也摆脱了内置脚本。

在这种情况下,在文档标题中还有另一个内置脚本,位于启动请求以加载CSS的行下方。如果移动此脚本,使其位于加载CSS的行的前面,则将消除解析器锁定。如果您使用Chrome开发者工具再次分析页面,则差异将非常明显。


在更改页面代码之前,HTML解析器在遇到内置脚本后停在1939行,并在此处停留了大约一秒钟。经过优化之后,他能够到达第5281行。

先前,解析器在第1939行停止并等待CSS加载,但现在到达第5281行。在页面的最后,还有另一个内置脚本再次停止了解析器。

这是解决该问题的快速方法。这不是代表问题最终解决方案的选项。更改元素的顺序并摆脱模式preload/polyfill只是第一步。您可以通过在页面中嵌入关键的CSS代码而不是从外部文件加载代码来最好地解决此问题。图案preload/polyfill设计用于除内联CSS外。这使我们可以完全忽略与脚本相关的问题,并确保在第一个请求之后,浏览器具有呈现页面所需的所有样式。

但就目前而言,应该指出的是,通过对项目的加载方式和DOM中元素的顺序进行很小的更改,我们可以实现良好的性能提升。

摘要


  • 如果使用loadCSS模式preload/polyfill,请转到样式加载模式print
  • 如果您以通常的方式(即使用指向这些样式文件的常规链接)加载外部样式,请将所有可以移动到链接上方的内置脚本移至加载样式。
  • 在页面中嵌入关键样式,以确保尽快开始渲染。

亲爱的读者们!您是否遇到过由于CSS导致页面渲染速度变慢的问题?

PS
RUVDS在3月8日向所有IT专业人员表示祝贺!
今年,我们决定不给郁金香,也不选择极客礼物。我们采取了不同的方法,创建了IT is female页面,以显示IT领域女性专家的身影。


All Articles