为您的(关键)CSS 添加注释

Avatar of Wladston Viana Ferreira Filho
Wladston Viana Ferreira Filho

DigitalOcean 提供适用于您旅程各个阶段的云产品。立即开始使用 $200 免费信用额度!

以下是 Wladston Ferreira Filho 的客座文章。我们之前已经介绍过关键 CSS,但 所涵盖的技术 特定于 SCSS。在这里,Wladston 涵盖了基础知识,并介绍了一种使用 PostCSS 的另一种方法,并展示了结果的示例(带数据)。

关键 CSS 是一种有效但使用并不普遍的方法,用于提高页面渲染性能。关键 CSS 包含以下两部分

  1. 延迟加载主样式表
  2. 内联最重要的(“可视区域以上”)样式

实现关键 CSS 需要一些工作。首先,这意味着将 CSS 分为两部分(关键部分和其余部分),这可能导致维护问题。

在本文的后面部分,我们将介绍一种工具,该工具可以通过在主 CSS 文件中基于注释(即 /* 注释 */)自动分割 CSS 来解决此缺点。

事实

来自 MozillaAkamai许多其他来源 的研究证实:页面渲染时间的小幅变化会显著影响性能指标。在网络连接较差的情况下,页面性能变得更加重要,因为下载时间可能会高出好几倍。

Google 甚至提供 一项服务 来为页面提供“速度评分”,这是一种委婉的暗示,性能可能与 SEO 相关。Google 建议适当地使用关键 CSS 来提高您的评分。这项技术一定会对渲染速度产生积极影响。渲染时间减少的程度取决于关键 CSS 的大小以及主样式表的大小。

关键 CSS 的工作原理

CSS 的“常规”方法是在 <head> 中包含您的主样式表作为 <link>。下载和解析该样式表会阻止渲染。关键 CSS 通过绕过阻止使页面更快地渲染。

第一步是“内联”(在 <head> 中使用简化的 <style> 标签)渲染页面可视区域以上内容所需的必要 CSS。这将使第二步成为可能:非关键 CSS 可以异步加载(非阻塞),同时网页正在渲染。当大型 CSS 文件到达后,它将通过 JavaScript 附加到页面。

在实践中实现此功能的一种常见方法是使用 CSS 预处理器。预处理器可以检测 CSS 中专门编写的注释,因此它可以区分关键 CSS 并自动将其分离。这在 CSS-Tricks 中已有介绍,使用 SCSS。让我们探索在原生 CSS 语法中实现此功能。

注意:要使此功能生效,您将需要一个服务器端工具,将关键 CSS 内联到您提供的所有页面中,以及添加几行内联 JavaScript 来加载主(非关键)样式表。

关键 CSS 的现有技术

关键 CSS 最终需要拥有两个独立的 CSS 片段:关键和非关键。我们如何获得它们?

完全手动:维护两个 CSS 文件

使用此策略,您可以直接编辑两个 CSS 文件而不是一个文件。虽然此策略很简单,不需要任何工具,但它更难使用。更难理解、阅读和更改样式。仅建议用于不太可能更改的静态 CSS。

完全自动化

服务器端工具(例如 Google Page Speed Extension)将自动检测哪些 CSS 是渲染可视区域以上内容所需的,它们会将它们选择的关键 CSS 分离并将其内联到您的页面中,无需您的干预。

这种技术有一些缺点:您自动生成的非关键 CSS 可能在评估的每个页面中都会发生变化,从而降低 CSS 缓存的效率。它也不能完美地检测关键 CSS,特别是对于小屏幕。

此外,您无法自定义或微调该过程。

SCSS 与 Jacket 插件

如果您使用 SCSS,您可以 安装 Jacket 插件 (详细信息请参见此处)。Jacket 会将使用特殊关键类标记的 CSS 分离到另一个文件中,在处理 LCSS 后生成关键 CSS 和非关键 CSS。这种技术的问题在于它将您绑定到 SCSS。如果您决定停止使用它,或者您想要更改预处理风格,您将需要额外的工作来调整您的关键 CSS 解决方案。

我的技术:PostCSS 和 PostCSS-Split

我的技术依赖于使用简单的纯 CSS 注释标记所有关键 CSS 声明。让我们考虑这个超级简单的 HTML 来进行说明

<!DOCTYPE html>
<html lang="en">
<body>
  <header>
    <h1>I'm a big header</h1>
  </header>
  <p>I'm below the fold</p></body>
</body>
</html>
header > h1 { 
  /* !Critical */ margin: 300px;
}
p { 
  border: 1px dotted black;
}

第一步是通过在其中放置 /* !Critical */ 来标记渲染可视区域以上内容所需的 CSS 规则。

为了确定主样式表中的哪些声明应该包含在关键 CSS 中,您可以从免费服务(例如 此服务)中获取建议。

在将“关键”注释放置到基本 CSS 文件中后,使用 npm 安装 PostCSS-Split。如果您尚未安装,则需要 安装 Node.js。在终端中,发出以下命令来安装 PostCSS-Split

sudo npm install -g postcss-split

然后,您可以发出以下命令,将已添加注释的基本 CSS 文件传递给 PostCSS-Split

postcss-split base.css

将根据您的输入文件创建全新的 base-critical.cssbase-non-critical.css 文件。`base-critical.css` 的内容将插入 <head> 中的 <style> 标签中。

至于加载 base-non-critical.css,您可以使用异步 CSS 加载器。例如,在 </body> 标签之前添加以下内容(并相应地更改 <your_css_url>

<script>
function lCss(u, m) {
  var l = document.createElement('link');
  l.rel = 'stylesheet';
  l.type = 'text/css';
  l.href = u;
  l.media = m;
  document.getElementsByTagName('head')[0].appendChild(l)
}
function dCss() {
  lCss('<your_css_url>', 'all')
}
if (window.addEventListener) {
  window.addEventListener('DOMContentLoaded', dCss, false)
} else { 
  window.onload=dCss
}
</script>

任何关键 CSS 技术的潜在缺陷

使用任何关键 CSS 技术时,您可能会遇到一些问题。让我们看看如何解决它们

优先级

如果有多个 CSS 规则具有相同的特异性,则声明在后面的规则将优先于声明在前面的规则。

请记住,您指定为关键的 CSS 会改变其位置:它将内联到您的 <head> 中,这意味着它将首先加载,并且会被随后加载的任何具有相同特异性选择器的 CSS 覆盖。

如果您使用这种方法在使用关键 CSS 时无法获得正确的 CSS 样式,请确保您的 CSS 不依赖于顺序。如果您遇到奇怪的结果,请使用 CSS 检查器来帮助您 解决特异性问题

FOUC

如果你的关键 CSS 没有包含渲染所有页面可见内容所需的 *所有* 规则,或者用户在你的大部分 CSS 加载之前开始浏览页面不可见内容,你就会遇到 FOUC(未样式内容闪烁)效应。

当你的非关键 CSS 加载时,浏览器会改变你页面的样式,以应用非关键 CSS 中的规则。这种样式变化的“闪烁”可能是不希望看到的。

为了缓解这种尴尬,一种方法是使用 CSS transition 来平滑地从无样式状态过渡到有样式状态。在开发过程中,你可以手动在注入你的大部分 CSS 的 JavaScript 代码中添加延迟。

在 HTML 页面中包含关键 CSS

你需要一个工具将关键 CSS 注入你的 HTML 页面的 <head> 中。如果你使用的是像 PHP 这样的后端语言,你可以使用 include() 语句(或类似方法)轻松实现。

<!DOCTYPE html>
<html lang="en">

<head>

  ...

  <style>
    <?php include_once("/path/to/base-critical.css"); ?>
  </style>

  ...

如果你没有直接处理代码(例如,你正在使用 WordPress 等内容管理系统),你可以搜索一个配置设置或插件来完成这项工作。在 WordPress 中,你可以添加一个“钩子”将你的 CSS 文件内容内联到最终的 HTML 中。

Jeremy Keith 在 这里 使用 Grunt/Twig 概述了一种方法。

这真的值得吗?

总结…

以下是实现这种技术所需的步骤。

  • 在你的主要样式表中识别并标记你的关键 CSS。
  • 在你的部署流程中添加一个任务,将基础 CSS 拆分为两个文件。
  • 添加额外的 JavaScript 代码来异步加载你的主要样式表。
  • 实现一个服务器端包含功能,将你的关键 CSS 内容添加到每个页面的 <head> 中。

案例研究:使用关键 CSS 的实际网站

我已经对 https://code.energy 网站进行了编程,使其可以提供带有或不带有关键 CSS 的页面。默认情况下,它将使用关键 CSS,除非包含 nocritical 查询字符串(例如,https://code.energy?nocritical)。另一种禁用关键 CSS 的方法是传递一个包含字符串 nocritical 的用户代理头。

有了这些,我们可以轻松地使用在线工具(如 webpagetest.org)来衡量关键 CSS 对这个网站速度性能的影响。Webpagetest 允许我们轻松地使用自定义用户代理字符串运行测试。以下结果来自每个场景 5 次实验的平均值。

关键 加载时间 开始渲染 完全加载 速度指数
x 0.949 秒 0.988 秒 1.003 秒 1036
0.838 秒 0.695 秒 0.893 秒 755

最令人印象深刻的差异是“开始渲染”时间。通过异步加载 CSS,我们可以看到浏览器能够并行发出更多请求,因为它更早地开始解析 HTML,正如你在这里看到的。

结论

如果你想要为你的网站获得最佳性能,你需要一个关键 CSS 策略。使用 PostCSS-Split,你可以以很小的维护成本实现它。