Help us understand the problem. What is going on with this article?

Above the fold(ファーストビュー)に含まれる CSS を webpack を使ってインライン化する

More than 1 year has passed since last update.

本投稿のサンプルコードを GitHub に用意しました。
im36-123/critical_css | GitHub

概要

前回は、 Critical CSS のインライン化までのプロセスを手順を追って説明しました。
詳しくは下記を御覧ください。
Above the fold(ファーストビュー)に含まれる CSS をインライン化してパフォーマンスの改善を図る

Critical CSS を開発者が手動で抽出しインライン化するのは現実的ではありません。
そこで、今回は webpack, と HTML Critical Webpack Plugin を使ってインライン化してみたいと思います。

環境構築

HTML Critical Webpack Plugin が動作する環境を構築します。
今回作る環境は下記に用意してあります。
im36-123/critical_css | GitHub

ところどころ説明を省略していますので、合わせて御覧ください。

インストール

HTML Critical Webpack Plugin の他に Mini CSS Extract PluginHTML Webpack Plugin をインストールします。

$ npm install --save-dev html-critical-webpack-plugin html-webpack-plugin mini-css-extract-plugin

HTML Critical Webpack PluginMini CSS Extract PluginHTML Webpack Plugin が実行されたあとに動作します.

webpack.config.js を書く

今回は src ディレクトリ直下に html, css, js の各ファイルを設置し、 Critical CSS がインライン化されたものを dist ディレクトリに書き出すようにします。

Mini CSS Extract PluginHTML Webpack Plugin の設定に関しては各プラグインのドキュメントを参考にしてください。

webpack.config.js の全体は こちら にあります。

webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HtmlCriticalWebpackPlugin = require("html-critical-webpack-plugin");

module.exports = {
  ・・・
  plugins: [
    new HtmlWebpackPlugin({・・・}),
    new MiniCssExtractPlugin({・・・}),
    new HtmlCriticalWebpackPlugin({
      base: path.join(path.resolve(__dirname), "dist/"),
      src: "index.html",
      dest: "index.html",
      inline: true,
      width: 375,
      height: 565,
      penthouse: {
        blockJSRequests: false
      }
    })
  ],
  ・・・
};

Above the fold(ファーストビュー) の領域を width, height で指定してください。

HTML, CSS を用意する

HTML と CSS を src ディレクトリ内に用意します。
今回は HTML は下記のように、 CSSは Bootstrap を使用しました

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>criticalCss</title>
</head>
    <body>
        <h1>クリティカル CSS を抽出する</h1>
    </body>
</html>

バンドルする

環境を作り終わったらバンドルして HTML ファイルの中身を見比べてみましょう。
バンドル前は下記でしたね。

バンドル前のindex.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>criticalCss</title>
</head>
    <body>
        <h1>クリティカル CSS を抽出する</h1>
    </body>
</html>

バンドル後のファイルは下記のようになっていると思います。

バンドル後のindex.html
<!DOCTYPE html>
<html><head><style>
    :root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar}@-ms-viewport{width:device-width}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}h1{margin-top:0;margin-bottom:.5rem}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}h1{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}h1{font-size:2.5rem}
</style>
<link href="main.css" rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link href="main.css" rel="stylesheet"></noscript>
<script>!function(n){"use strict";n.loadCSS||(n.loadCSS=function(){});var o=loadCSS.relpreload={};if(o.support=function(){var e;try{e=n.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),o.bindMediaToggle=function(t){var e=t.media||"all";function a(){t.media=e}t.addEventListener?t.addEventListener("load",a):t.attachEvent&&t.attachEvent("onload",a),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(a,3e3)},o.poly=function(){if(!o.support())for(var t=n.document.getElementsByTagName("link"),e=0;e<t.length;e++){var a=t[e];"preload"!==a.rel||"style"!==a.getAttribute("as")||a.getAttribute("data-loadcss")||(a.setAttribute("data-loadcss",!0),o.bindMediaToggle(a))}},!o.support()){o.poly();var t=n.setInterval(o.poly,500);n.addEventListener?n.addEventListener("load",function(){o.poly(),n.clearInterval(t)}):n.attachEvent&&n.attachEvent("onload",function(){o.poly(),n.clearInterval(t)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:n.loadCSS=loadCSS}("undefined"!=typeof global?global:this);</script></head>

<head>
    <meta charset="UTF-8">
    <title>criticalCss</title>

    </head><body>
        <h1>クリティカル CSS を抽出する</h1>
    <script type="text/javascript" src="main.js"></script></body>
</html>

バンドル後のファイルを見ると下記のことがわかります。

  • <style></stype> 内に Above the fold に必要なルールセットが書き出されている。
  • CSS ファイルは非同期で読み込まれている (rel="preload")。
    • loadCSS を使って preload が動かないブラウザでも非同期で CSS ファイルを読み込めるようになっている。

まとめ

自社サイトを Chrome の audit で解析しているときに、 Critical CSS について知ったので書きました。

参考

anthonygore/html-critical-webpack-plugin
CSS の配信を最適化する | PageSpeed Insights | Google Developers

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away