LoginSignup
11
8

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-09-15

本投稿のサンプルコードを 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

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8