天天看点

如何用 Critical CSS 做前端性能极致优化?

作者:高级前端进阶

大家好,很高兴又见面了,我是"高级前端‬进阶‬",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。

如何用 Critical CSS 做前端性能极致优化?

1.为什么开始谈论关键路径 CSS

关键路径 CSS 很重要

构建页面的渲染树需要 CSS,而 JavaScript 在页面的初始构建过程中通常会阻塞 CSS。 因此,开发者应该确保任何非必要的 CSS 都被标记为非关键(例如:打印和其他媒体查询),并且关键 CSS 体积应该足够小,同时加载时间要尽可能短。

如何用 Critical CSS 做前端性能极致优化?

借助于关键 CSS,用户可以尽快获得基本样式;因为它就在文档的<head>中的<style>元素中,所以没有额外的请求到服务器获取样式表&等待请求的样式加载和渲染。

如何用 Critical CSS 做前端性能极致优化?

然后在后台加载下面的折叠或非关键样式,以避免渲染阻塞。

关键路径 CSS 应该内联

为了获得最佳性能,开发者可能需要考虑将关键 CSS 直接内联到 HTML 文档中。

如何用 Critical CSS 做前端性能极致优化?

内联 CSS 消除了关键路径中的额外往返,如果正确设置,可用于提供“单次往返”关键路径长度,其中只有 HTML 是阻塞资源。

2.critical.js 基础用法

critical.js 用于从 HTML 中提取关键内容并内联关键路径 CSS,比如首屏。critical.js 在 Github上开源,有超过10k的star,同时使用也非常简单:

import { generate } from 'critical';           

同时支持以下配置选项:

generate({
  // Inline the generated critical-path CSS
  // - true generates HTML
  // - false generates CSS
  inline: true,
  // Your base directory
  base: 'dist/',
  // HTML source
  html: '<html>...</html>',
  // HTML source file
  src: 'index.html',
  // Your CSS Files (optional)
  css: ['dist/styles/main.css'],
  // Viewport width
  width: 1300,
  // Viewport height
  height: 900,
  // Output results to file
  target: {
    css: 'critical.css',
    html: 'index-critical.html',
    uncritical: 'uncritical.css',
  },
  // Extract inlined styles from referenced stylesheets
  extract: true,
  // ignore CSS rules
  ignore: {
    atrule: ['@font-face'],
    rule: [/some-regexp/],
    decl: (node, value) => /big-image\.png/.test(value),
  },
});           

下面的示例用于生成关键路径 CSS,是最基本的用法示例:

generate({
  base: 'test/',
  src: 'index.html',
  target: 'styles/main.css',
  width: 1300,
  height: 900,
});           

下面示例生成并压缩关键路径 CSS:

generate({
  base: 'test/',
  src: 'index.html',
  target: 'styles/styles.min.css',
  width: 1300,
  height: 900,
});
           

而下面的示例用于生成、压缩和内联关键路径 CSS:

generate({
  inline: true,
  base: 'test/',
  src: 'index.html',
  target: {
    html: 'index-critical.html',
    css: 'critical.css',
  },
  width: 1300,
  height: 900,
});           

值得一提的是,generate 方法支持 callback 和 promise 两种方式返回处理后的结果,比如下面是 promise 的方式:

generate({
    base: 'test/',
    src: 'index.html',
    width: 1300,
    height: 900
}).then((({css, html, uncritical})) => {
    // You now have critical-path CSS as well as the modified HTML.
    // Works with and without target specified.
    ...
}).error(err => {
    ...
});           

3.critical.js 高级用法

生成具有多种分辨率的关键路径 CSS

当网站是自适应,并且开发者想要为多种屏幕分辨率提供关键 CSS 时,这是一个有用的选项。 但是值得注意的是,最终输出将被压缩,以消除重复的规则包含。

generate({
  base: 'test/',
  src: 'index.html',
  target: {
    css: 'styles/main.css',
  },
  dimensions: [
    {
      height: 200,
      width: 500,
    },
    {
      height: 900,
      width: 1200,
    },
  ],
});           

生成关键路径 CSS 并忽略特定选择器

当开发者想要推迟加载网络字体或背景图像,可以通过下面的方式:

generate({
  base: 'test/',
  src: 'index.html',
  target: {
    css: 'styles/main.css',
  },
  ignore: {
    atrule: ['@font-face'],
    decl: (node, value) => /url\(/.test(value),
  },
});
           

更多高级用法可以参考文末资料,本文不再过多展开。

4.critical.js 的可行替代方案

值得一提的是 Penthouse,同时 FilamentGroup 还维护一个关键 CSS 节点模块,类似于 Penthouse ,其会查找并输出页面的关键路径 CSS。

当开发者启用 Priority_ritic_css 过滤器时,适用于 nginx、apache、IIS、ATS 和 Open Lightspeed 的 PageSpeed Optimization 模块可以自动完成所有繁重的工作。

本文重点关注下 Penthouse,其他类似模块可以在文末参考资料中获取。Critical 和 Penthouse 之间的主要区别包括以下几个点:

  • Critical 会自动从 HTML 中提取样式表,并生成关键路径 CSS,而其他模块通常要求开发者预先指定这一点。
  • Critical 提供了内联关键路径 CSS 的方法(生成 CSS 后的常见下一步逻辑)
  • 由于同时处理生成和内联,因此能够抽出一些丑陋的样板文件,否则需要分别解决这些问题

总之,如果网站或应用程序有大量样式需要动态注入到 DOM 中(有时在 Angular 应用程序中很常见),建议直接使用 Penthouse。 但是 Penthouse 要求开发者预先提供样式,某些情况下可能比 Critical 提供更高的准确性。

参考资料

https://github.com/addyosmani/critical

https://github.com/addyosmani/critical-path-css-demo

https://github.com/filamentgroup/criticalCSS

https://github.com/pocketjoso/penthouse

https://www.tezify.com/how-to/defer_css_loading_with_loadcss/

https://web.dev/articles/extract-critical-css?hl=zh-cn