大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。
1.为什么开始谈论关键路径 CSS
关键路径 CSS 很重要
构建页面的渲染树需要 CSS,而 JavaScript 在页面的初始构建过程中通常会阻塞 CSS。 因此,开发者应该确保任何非必要的 CSS 都被标记为非关键(例如:打印和其他媒体查询),并且关键 CSS 体积应该足够小,同时加载时间要尽可能短。
借助于关键 CSS,用户可以尽快获得基本样式;因为它就在文档的<head>中的<style>元素中,所以没有额外的请求到服务器获取样式表&等待请求的样式加载和渲染。
然后在后台加载下面的折叠或非关键样式,以避免渲染阻塞。
关键路径 CSS 应该内联
为了获得最佳性能,开发者可能需要考虑将关键 CSS 直接内联到 HTML 文档中。
内联 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