云和安全管理服务专家新钛云服 魏建民翻译
结合现代网站的复杂性和浏览器处理 CSS 的方式,即使是适量的 CSS 也可能成为处理受限设备、网络延迟、带宽或数据限制的人的瓶颈。
由于性能是用户体验的重要组成部分,因此必须确保跨各种形状和大小的设备提供一致、高质量的体验,这也需要优化 CSS。
这篇文章将介绍 CSS 会导致哪些类型的性能问题,以及制作不会妨碍人们的 CSS 的最佳实践。
一、CSS 是如何工作的?
1、CSS 块渲染
当页面有可用的 CSS 时,无论是内联样式表还是外部样式表,浏览器都会延迟呈现,直到 CSS 被解析。这是因为没有 CSS 的页面通常无法使用。
如果浏览器向你展示了一个没有 CSS 的凌乱页面,那么片刻之后就会进入一个有样式的页面,不断变化的内容和突然的视觉变化会让用户体验变得混乱。糟糕的用户体验有一个名字——无样式内容闪现(FOUC)
2、CSS 可以阻止 HTML 解析
尽管浏览器在解析完 CSS 之前不会显示内容,但它会处理 HTML 的其余部分。但是,脚本会阻止解析器,除非它们被标记为defer或async。脚本可能会操纵页面和其余代码,因此浏览器必须注意该脚本何时执行。
解析器阻塞脚本:脚本如何阻塞 HTML 解析。
因为脚本会影响应用到页面的样式,如果浏览器仍在处理一些 CSS,它会等到它完成后再运行脚本。由于在脚本运行之前它不会继续解析文档,这意味着 CSS 不再只是阻止渲染——根据外部样式表的顺序,文档中的脚本也可能会停止 HTML 解析。
Parser Blocking CSS:CSS 如何阻止 HTML 解析。
为避免阻塞解析,请尽快交付 CSS 并以最佳顺序安排您的资源。
二、观察 CSS 的大小
1、压缩和缩小 CSS
建立连接去下载外部样式表不可避免地会导致延迟,但您可以通过最小化通过网络传输的总字节数来加快下载速度。
压缩文件可以显着提高速度,许多托管平台和 CDN 默认使用压缩编码资产(或者您可以轻松配置它们)。服务器和客户端交互使用最广泛的压缩格式是Gzip。还有Brotli可以提供更好的压缩结果,尽管它不像 Gzip 那样受支持。
缩小CSS是删除空格和任何不需要的代码的过程。输出是一个较小但完全有效的代码文件,浏览器可以解析它,这将为您节省一些字节。Terser是一种流行的 JavaScript 压缩工具,或使用webpack,v4 插件来创建缩小的构建文件。
2、删除未使用的 CSS
使用 CSS 框架时,以未使用的 CSS 结束是比较常见的(除非我们只包含我们需要的组件)。同样的问题出现在长期增长的大型代码库中。
删除未使用的 CSS 通常是手动工作。主要的挑战是它有多复杂。我们必须在所有可能的状态下,在所有可能的设备(涵盖媒体查询)上仔细审核整个站点,并执行所有可能改变样式的 JavaScript 功能。
UnusedCSS 和 PurifyCSS 是可以帮助确定不必要样式的流行工具,但我们应该将它们与仔细的视觉回归测试配对。
这就是使用 CSS-in-JS 的显着优势:每个组件中呈现的样式仅需要 CSS。快速 CSS-in-JS 的秘诀是将 CSS 内联到页面中或将其提取到外部 CSS 文件中。在 JavaScript 文件中传送 CSS 将导致它被解析和评估缓慢。
三、优先考虑关键 CSS
关键CSS是一种为首屏内容提取和内联CSS的技术。在HTML文档的<head>中内联提取的样式,无需发出额外的请求来获取这些样式并加快渲染速度。
为了最大限度地减少首次渲染的往返次数,请将首屏内容保持在14 KB(压缩)以下。
确定关键 CSS 并不完全准确,因为您需要对折叠位置(因设备屏幕尺寸而异)做出假设。这对于高度动态的站点来说可能很困难。即使不精确,它仍然可以带来性能改进,我们可以使用Critical、CriticalCSS和Penthouse等工具将其自动化。
1、异步加载 CSS
CSS的其余部分(不太重要的部分)最好异步加载。实现这一点的方法是将链接媒体属性设置为print:
“打印”媒体类型定义了用户尝试打印页面时的样式表规则,浏览器将加载此类样式表而不会延迟页面渲染。将该样式表应用于所有媒体(即屏幕而不仅仅是打印)使用onload属性在样式表完成加载时将媒体设置为全部。
另一种选择是使用<link rel="preload">(而不是rel="stylesheet")来实现与上述类似的模式,并在加载事件时将rel属性切换到样式表。使用这种方法时需要考虑一些缺点。
· 浏览器对预加载的支持仍然不是很好,因此需要一个 polyfill(或使用诸如loadCSS 之类的库)来跨浏览器应用样式表。
· 预加载很早就以最高优先级获取文件,可能会降低其他重要下载的优先级。
如果你确实想要preload提供的高优先级获取(在支持它的浏览器中),loadCSS 的创建者建议你将它与第一个模式结合起来,像这样:
2、避免在 CSS 文件中使用 @import
在 CSS 文件中使用@import会减慢渲染速度。首先,浏览器必须下载 CSS 文件以发现导入的资源,然后在渲染之前发起另一个下载请求。
如果你有一个包含@import url(imported.css) 的样式表;网络瀑布如下所示:
在链接元素中加载两个样式表允许并行下载:
四、使用高效的 CSS 动画
当您为页面上的元素设置动画时,浏览器通常必须重新计算它们在文档中的位置和大小,这会触发布局。例如,如果您更改元素的宽度,则其任何子元素都可能受到影响,并且页面布局的很大一部分可能会更改。布局几乎总是作用于整个文档,所以布局树越大,它执行布局计算的时间就越长。
为元素设置动画时,必须尽量减少布局和重绘。并非所有 CSS 动画技术都是平等的,现代浏览器可以最好地创建具有位置、缩放、旋转和不透明度的高性能动画:
· 不要更改高度和宽度属性,而是使用transform: scale()。
· 四处移动元素,请避免更改top、right、bottom或left属性并使用transform: translate()代替。
· 如果要模糊背景,请考虑使用模糊图像并更改其不透明度。
在包含 CSS属性告诉浏览器的元素及其后代被认为是独立于文档树(尽可能)。它将页面的子树与其余部分隔离开来。然后浏览器可以优化页面独立部分的渲染(样式、布局和绘制操作)以提高性能。
该包含属性是在包含许多独立的小部件页面有用。我们可以使用它来防止每个小部件内的更改在小部件的边界框之外产生副作用。一个主要是静态的站点不会从这个策略中获得什么好处。
五、使用 CSS 优化字体加载
1、在字体加载期间避免不可见的文本
字体通常是需要一段时间才能加载的大文件。一些浏览器会在字体加载之前隐藏文本(导致“不可见文本闪烁”或 FOIT)来解决这个问题。在优化速度时,您需要避免“不可见文本闪烁”,并立即使用系统字体(一种预装在他们机器上的字体)向人们显示内容。加载字体文件后,它将替换称为“无样式文本闪烁”或 FOUT 的系统字体。
实现此目的的一种方法是使用front-display 一种用于指定字体显示策略的API。使用带有值swap的font-display告诉浏览器使用该字体的文本应该立即使用系统字体显示。
2、使用可变字体来减小文件大小
可变字体使字体的许多不同变体能够合并到一个文件中,而不是为每个宽度、粗细或样式都设置一个单独的字体文件。它们允许您使用 CSS 和单个@font-face引用访问给定字体文件中的所有变体。
在需要多种字体变体的情况下,可变字体可以显着减小文件大小。您可以加载包含所有信息的单个文件,而不是加载常规和粗体样式及其斜体版本。
Monotype 进行了一项实验,通过组合 12 种输入字体来生成 8 个权重,跨越三种宽度,跨越斜体和罗马风格。在单个可变字体文件中存储 48 种独立字体意味着文件大小减少了 88%。
六、不用担心 CSS 选择器的速度
CSS 选择器的结构会影响浏览器匹配它们的速度。浏览器从右到左读取选择器,因此当您使用后代选择器时。例如,nav a {},它将首先匹配页面上的每个<a>元素,然后在nav内部的元素上归零。如果您使用更具体的选择器,例如,在 nav 元素内的每个<a>上使用.nav-link,它就不会花时间尝试匹配页面上的每个<a>。
如果您考虑浏览器如何从右到左匹配选择器以及.container ul li a { } 之类的示例,您就会明白为什么后代选择器通常被标记为“重要”的原因。
这样的选择器似乎是一个速度问题。但是,选择器匹配性能很快。CSS 声明对压缩算法非常友好,因此优化 CSS 选择器所需的工作通常最好花在应用程序的其他部分上,从而获得更高的投资回报。
CSS 对于加载页面和令人愉悦的用户体验至关重要。虽然我们通常可能会优先考虑其他资产(例如脚本或图像)的影响更大,但我们不应该忘记 CSS。通过上述策略,您将能够确保快速交付和执行。
原文:
https://calibreapp.com/blog/css-performance