本投稿のサンプルコードを 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 Plugin
と HTML Webpack Plugin
をインストールします。
$ npm install --save-dev html-critical-webpack-plugin html-webpack-plugin mini-css-extract-plugin
HTML Critical Webpack Plugin
はMini CSS Extract Plugin
とHTML Webpack Plugin
が実行されたあとに動作します.
webpack.config.js を書く
今回は src
ディレクトリ直下に html
, css
, js
の各ファイルを設置し、 Critical CSS
がインライン化されたものを dist
ディレクトリに書き出すようにします。
Mini CSS Extract Plugin
とHTML Webpack Plugin
の設定に関しては各プラグインのドキュメントを参考にしてください。
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
を使用しました
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>criticalCss</title>
</head>
<body>
<h1>クリティカル CSS を抽出する</h1>
</body>
</html>
バンドルする
環境を作り終わったらバンドルして HTML ファイルの中身を見比べてみましょう。
バンドル前は下記でしたね。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>criticalCss</title>
</head>
<body>
<h1>クリティカル CSS を抽出する</h1>
</body>
</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