What Is Critical CSS?
一个css请求可以显著增加页面渲染时间。原因是:默认情况下浏览器会在所有标签里的css文件下载、编译、执行完后再渲染页面。浏览器这么做是因为需要计算页面布局。
不幸的是,这就意味着,如果我们有一个非常大的css文件,并且这个文件需要花时间去下载,那我们的用户就会等不及直接关闭页面了。不过,幸运的是,有一个tricky可以允许我们优化css文件的传输时间,减少阻塞时间。这项技术就是:critical rendering path.
critical rendering path 展示了浏览器渲染一个页面需要的几个步骤。我们的目的是找到需要首屏呈现给用户的最少的阻塞css,或者叫做critical CSS,该技术背后的理念是:在一个TCP相应包里给用户首屏最重要的内容。为了让你有个清晰的认识,请看下图:
那么我们如何判断哪些是critical css?判断这个工作非常复杂,需要开发者去看这个页面的DOM结构,然后,需要判断这些DOM结构的styles 列表。手工的做这项工作,会非常大单调乏味且工作量巨大,但庆幸的是,现在有很多好用的工具,供我们选择。
在这篇文章,我将会介绍怎样使用Critical CSS技术去提升你的页面的渲染速度,并且介绍一个工具来实现自动化。
Critical CSS In Action
在使用这项技术之前,我们需要先改变我们编写css的方式——将css 分成2个文件,第一个css文件:只有critical css,然后inline 插入页面中。第二个css文件:放其他css,异步加载。
将css inline嵌入HTML,允许我们在一次请求中把css送给用户。在html中看起来就是这样子的:
<!doctype html>
<head>
<style> /* inlined critical CSS */ </style>
<script> loadCSS('non-critical.css'); </script>
</head>
<body>
...body goes here
</body>
</html>
上面代码,我们先使用style标签将css嵌入html中,然后,使用loadCSS()方法异步加载其他的css文件。这个非常重要,因为我们的目的就是将较大量的css卸载出来。
如何将每个页面的critical css提取出来,然后嵌入到html中,看似工作量巨大,是一个噩梦。但是有个好消息,我们现在有工具,可以自动实现:Critical,这是一个node.js包,作者是Addy Osmani criticalCSS
这些包怎么使用大家可以去github上看官网文档,不翻译了,毕竟大过年的,我还要逗猫呢。。。新年快乐~
原文链接:点击这里
webpack 中使用
注:由于本人在前端工程化中,经常使用的工具是webpack,在webpack中已有成熟插件可以使用:html-critical-webpack-plugin,github地址:这里
配置代码示例:(具体使用,可异步官方文档)
const HtmlCriticalWebpackPlugin = require("html-critical-webpack-plugin");
module.exports = {
mode: 'production',
plugins: [
new HtmlWebpackPlugin({ ... }),
new MiniCssExtractPlugin({ ... }),
new HtmlCriticalWebpackPlugin({
base: path.resolve(__dirname, 'dist'),
src: 'index.html',
dest: 'index.html',
inline: true,
minify: true,
extract: true,
width: 375,
height: 565,
penthouse: {
blockJSRequests: false,
}
})
]
...
};
在不使用critical 插件时,webpack构建之后的html入口文件是这样的:(使用link引入css文件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>vuestrap-code-split</title>
<link href="/style.css" rel="stylesheet">
</head>
<body>
<!--App content goes here, omitted for brevity.-->
<script type="text/javascript" src="/build_main.js"></script>
</body>
</html>
当使用完该插件后,webpack构建完之后的html入口文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Bootstrap Critical</title>
<style type="text/css">
/* 关键CSS通过内部样式表方式引入 */
body {
font-family: Helvetica Neue,Helvetica,Arial,sans-serif;
font-size: 14px;
line-height: 1.42857;
color: #333;
background-color: #fff;
}
...
</style>
<link href="/style.96106fab.css" rel="preload" as="style" onload="this.rel='stylesheet'">
<noscript>
<link href="/style.96106fab.css" rel="stylesheet">
</noscript>
<script>
/*用来加载非关键CSS的脚本*/
</script>
</head>
<body>
<!-- 这里是App的内容 -->
<script type="text/javascript" src="/build_main.js"></script>
</body>
</html>
可见critical css 已经使用style标签内联嵌入了,link标签引入的css文件,也使用了rel="preload",preload不阻塞渲染,无论资源是否加载完成,浏览器都会接着绘制页面。