CSS和网络性能

CSS对于呈现页面至关重要 - 在找到,下载和解析所有CSS之前,浏览器不会开始呈现 - 因此我们必须尽可能快地将其加载到用户的设备上。 关键路径上的任何延迟都会影响我们的“开始渲染”并让用户看到空白屏幕。

什么是大问题?

从广义上讲,这就是CSS对性能至关重要的原因:

  • 浏览器在构建渲染树之前无法渲染页面;
  • 渲染树是DOM和CSSOM的组合结果;
  • DOM是HTML加上需要对其进行操作的任何阻塞JavaScript;
  • CSSOM是针对DOM应用的所有CSS规则;
  • 使用async和defer属性很容易使JavaScript无阻塞;
  • CSS不容易异步;
  • 所以要记住的一个好的经验法则是,您的页面会在你最慢的样式表加载完成之后才展示。

考虑到这一点,我们需要尽快构建DOM和CSSOM。 在大多数情况下,构建DOM相对较快:您的第一个HTML响应是DOM。 但是,由于CSS几乎总是HTML的子资源,因此构建CSSOM通常需要更长的时间。

在这篇文章中,我想看看CSS如何证明是网络上的一个重大瓶颈(本身和其他资源)以及我们如何缓解它,从而缩短关键路径并缩短开始渲染的时间。

使用关键CSS

如果你有能力,减少Start Render时间的最有效方法之一就是使用Critical CSS模式:识别Start Render所需的所有样式(通常是首屏所需的样式), 将它们内联到文档的中的

将产生这个瀑布图:

由于无效预装载扫描程序导致Firefox失去并行化(N.B.在IE / Edge中出现相同的瀑布。)

这个问题的直接解决方案是交换

在所有浏览器中都存在一种有意和预期的迷人行为,但我从未遇到过一个了解它的开发人员。 当您考虑它可以带来的巨大性能影响时,这是非常令人惊讶的:

如果有任何当前CSS在加载,浏览器将不会执行

这是设计的。 这是故意的。 当前正在下载任何CSS时,HTML中的任何同步

根据这个顺序,我们可以清楚地看到JavaScript文件甚至在构建CSSOM之前甚至没有开始下载。 我们完全失去了任何并行化:

在异步代码段之前使用样式表可以撤消我们并行化的机会。
有趣的是,Preload Scanner希望提前获得对analytics.js的引用,但是我们无意中隐藏了它:“analytics.js”是一个字符串,并且在<<之前不会成为可标记的src属性 script>元素存在于DOM中。 这是我早些时候说的,当我稍后再说这个时。

第三方供应商提供这样的异步代码片段以更安全地加载脚本是很常见的。 开发人员对这些第三方持怀疑态度,并在页面后面放置异步片段也是很常见的。 虽然这是出于最好的意图 - 我不想在我自己的资产之前放置第三方

交换样式表和异步代码片段可以重新获得并行化。

现在您可以看到我们已经完全重新获得了并行化,并且页面加载速度提高了近2倍。

在CSS之前放置任何非CSSOM查询JavaScript; 在CSS之后放置任何CSSOM查询JavaScript

更进一步,除了异步加载片段之外,我们应该如何更普适地加载CSS和JavaScript? 为了解决这个问题,我提出了以下问题并从那里开始工作:

如果:

  • 在CSSOM构造上阻止CSS后定义的同步JS;
  • 同步JS阻止DOM构造

那么 - 假设没有相互依赖 - 哪个更快/更喜欢?

Script -> style;
style -> script?

答案是:

如果文件不相互依赖,那么您应该将阻塞脚本置于阻塞样式之上 - 没有必要将JavaScript执行延迟到JavaScript实际上不依赖的CSS。

(Preload Scanner确保即使在脚本上阻止了DOM构造,CSS仍然会并行下载。)

如果你的一些JavaScript做了但有些不依赖于CSS,那么加载同步JavaScript和CSS的绝对最佳顺序是将JavaScript分成两部分并将其加载到CSS的任何一侧:







使用这种加载模式,我们可以按最佳顺序进行下载和执行。 我为下面的截图中的微小细节道歉,但希望你能看到代表JavaScript执行的小粉红色标记。
entry(1)是计划在其他文件到达和/或执行时执行某些JavaScript的HTML;
entry(2)执行它到达的那一刻;
entry(3)是CSS,所以不执行任何JavaScript;
在CSS完成之前,entry(4)实际上不会执行。

注: 您必须根据自己的特定用例测试此模式:根据您之前的CSS JavaScript文件与CSS本身之间的文件大小和执行成本是否存在巨大差异,可能会有不同的结果。 测试,测试,测试。

放在中

这个最终策略是一个相对较新的策略,对感知性能和渐进式渲染有很大好处。 它也非常友好。

在HTTP / 1.1中,我们将所有样式连接到一个主要包中是很典型的。 我们称之为app.css:





  




  

  

...

...

这带来三个关键的低效率:

  • 任何给定的页面只会使用app.css中的一小部分样式:我们几乎肯定会下载比我们需要的更多的CSS。
  • 我们受限于一种效率低下的缓存策略:例如,对仅在一个页面上使用的日期选择器上当前所选日期的背景颜色进行更改将需要我们缓存整个app.css。
  • 整个app.css阻止渲染:如果当前页面只需要17%的app.css并不重要,我们仍然需要等待其他83%才能开始渲染。

使用HTTP / 2,我们可以开始解决点(1)和(2):





  
  
  
  
  
  
  
  
  




  

  

...

...

现在我们正在解决冗余问题,因为我们能够加载更适合页面的CSS,而不是不加选择地下载所有内容。 这减少了关键路径上阻塞CSS的大小。

我们还可以采用更有意思的缓存策略,只缓存破坏需要它的文件,并保持其余部分不受影响。

我们还没有解决的问题是它仍然阻止渲染 - 我们仍然只有最慢的样式表。 这意味着如果无论出于何种原因,site-footer.css需要很长时间才能下载,浏览器无法开始渲染.site-header。

但是,由于Chrome最近发生了变化(我相信版本69),以及Firefox和IE / Edge中已经存在的行为, 只会阻止后续内容的呈现,而不是 整页。 这意味着我们现在能够像这样构建我们的页面:





  




  
  

  
  

...

...

这样做的实际结果是,我们现在能够逐步呈现我们的页面,在页面可用时有效地将页面输送样式添加到页面中。

在目前不支持这种新行为的浏览器中,我们不会遇到性能下降:我们会回到原来的行为,我们只有最慢的CSS文件加载完成才会展示页面。

总结

本文中有很多要消化的内容。 它最终超越了我最初打算写的帖子。 尝试总结加载CSS的最佳网络性能实践:

  • Lazyload Start Start Render不需要的任何CSS:

       拆分关键CSS;
       或将您的CSS拆分为媒体查询。
    
  • 避免@import:

       在你的HTML中;
       特别是在CSS中;
       并提防Preload Scanner的奇怪之处。
    
  • 警惕同步CSS和JavaScript命令:

       在CSSOM完成之前,CSS之后定义的JavaScript将无法运行
       所以如果你的JavaScript不依赖于你的CSS,在CSS之前加载它;
       如果它取决于你的CSS,在CSS之后加载它。
    
    
  • 在DOM需要时加载CSS,这将取消阻止“开始渲染”并允许渐进式渲染

我上面概述的所有内容都遵循规范或已知/预期的行为,但是,一如既往,自己测试一切。 虽然这在理论上都是正确的,但在实践中事情总是有所不同。 套用中国的一句老话,实践出真知啊

创建了一个程序员交流微信群,大家进群交流IT技术

如果已过期,可以添加博主微信号15706211347,拉你进群

你可能感兴趣的:(css,html,script,network)