(翻译)Critical CSS

1,616 阅读4分钟

What Is Critical CSS?

一个css请求可以显著增加页面渲染时间。原因是:默认情况下浏览器会在所有标签里的css文件下载、编译、执行完后再渲染页面。浏览器这么做是因为需要计算页面布局。

不幸的是,这就意味着,如果我们有一个非常大的css文件,并且这个文件需要花时间去下载,那我们的用户就会等不及直接关闭页面了。不过,幸运的是,有一个tricky可以允许我们优化css文件的传输时间,减少阻塞时间。这项技术就是:critical rendering path.

critical rendering path 展示了浏览器渲染一个页面需要的几个步骤。我们的目的是找到需要首屏呈现给用户的最少的阻塞css,或者叫做critical CSS,该技术背后的理念是:在一个TCP相应包里给用户首屏最重要的内容。为了让你有个清晰的认识,请看下图:

在上面的例子中,这个页面的critical css 就是用户在首屏中看到的部分。这就意味着我们可以只加载渲染页面顶部部分的css。而对于剩余部分的css,我们可以不用那么着急,可以采用异步地加载。

那么我们如何判断哪些是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不阻塞渲染,无论资源是否加载完成,浏览器都会接着绘制页面。