はじめに
クリティカルCSS(ファーストビューで必要なCSS)をHTMLファイルにインライン化する方法があります。本記事ではこの方法によるパフォーマンスへの影響を確認してみます。
ゴール
クリティカルCSSをHTMLファイルにインライン化したパターンと外部ファイルとして読み込むパターンにおいて、Largest Contentful Paint (LCP)の指標を比較し、パフォーマンスの違いを比較してみる。
環境
express:4.21.2
ディレクトリ構造
critical-css-demo/
├── package.json
├── server.js
└── src/
├── inline/
│ ├── index.html
│ └── styles.css
└── external/
├── index.html
└── styles.css
クリティカルCSSとは?
キーワード:クリティカル CSS は、ユーザーにコンテンツをできるだけ早く表示するために、スクロールせずに見える範囲のコンテンツの CSS を抽出する手法です。
引用:https://web.dev/articles/extract-critical-css
抽出したクリティカルをHTMLにインライン化して記載する事で、ファーストビューで使用するCSSのリクエストがなくなり、ページの表示を早くできます。
インライン化パターン
クリティカルCSSをHTMLにインライン化して記載してみます。
<!DOCTYPE html>
<html>
<head>
<title>Critical CSS Demo - Inline Version</title>
<style>
.first-view {
height: 100vh;
background: linear-gradient(45deg, #1a2a6c, #b21f1f);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
}
.first-view-text {
font-size: 1.5rem;
margin: 2rem 0;
}
.cta-button {
padding: 1rem 2rem;
font-size: 1.2rem;
background: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.3s;
}
</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
</head>
<body>
<div class="first-view">
<h1>サンプルテキスト</h1>
<p class="first-view-text">ほげほげテキスト</p>
<button class="cta-button">サンプルボタン</button>
</div>
<div class="content">
<div class="card">
<h2>カード1</h2>
<p>スクロールして表示されるコンテンツ</p>
</div>
<div class="card">
<h2>カード2</h2>
<p>これも初期ビューポート外</p>
</div>
</div>
</body>
</html>
参考:
- preload:https://web.dev/articles/preload-critical-assets#preloading_css_files
- noscript:https://web.dev/articles/defer-non-critical-css#optimize
ファーストビュー部分以外のCSS
.cta-button:hover {
transform: scale(1.05);
}
.content {
padding: 4rem 2rem;
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.card {
padding: 2rem;
background: #f5f5f5;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.card:hover {
transform: translateY(-5px);
}
外部CSS参照パターン
ファーストビューで使用するCSSは外部ファイルに記載します。
<!DOCTYPE html>
<html>
<head>
<title>Critical CSS Demo - External Version</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="first-view">
<h1>サンプルテキスト</h1>
<p class="first-view-text">ほげほげテキスト</p>
<button class="cta-button">サンプルボタン</button>
</div>
<div class="content">
<div class="card">
<h2>カード1</h2>
<p>スクロールして表示されるコンテンツ</p>
</div>
<div class="card">
<h2>カード2</h2>
<p>これも初期ビューポート外</p>
</div>
</div>
</body>
</html>
styles.css
.first-view {
height: 100vh;
background: linear-gradient(45deg, #1a2a6c, #b21f1f);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
}
.first-view-text {
font-size: 1.5rem;
margin: 2rem 0;
}
.cta-button {
padding: 1rem 2rem;
font-size: 1.2rem;
background: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.3s;
}
.cta-button:hover {
transform: scale(1.05);
}
.content {
padding: 4rem 2rem;
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.card {
padding: 2rem;
background: #f5f5f5;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.card:hover {
transform: translateY(-5px);
}
サーバ側
expressでサーバを準備します。
import express from 'express';
const app = express();
const port = 3000;
app.use(express.static('src'));
app.listen(port, () => {
console.log(`
サーバー起動\n
- インライン化パターン: http://localhost:${port}/inline/
- 外部CSS参照パターン: http://localhost:${port}/external/
`);
});
動作を確認してみる
サーバを立ち上げてブラウザからページを確認します。
npm start
ブラウザでページを開けたので DevToolsでLCPを確認してみます。
ネットワーク速度の違いでどれくらいパフォーマンスが変わるのか興味があったので、スロットリングさせて確認してみました。
スロットリングについて
DevToolsのNetworkタブにある項目で、ネットワーク速度を制限して検証できる機能を指しています。
参考:https://developer.chrome.com/docs/devtools/network/reference?hl=ja#throttling
ここでは測定結果を確認したものを一例として記載しています。
▼ スロットリングなし
- 外部CSS参照パターン(左画像):0.06s
- CSSをインライン化したパターン(右画像):0.06s
スロットリングさせてない状態だとLCP、FCPに差はそんなに見られませんでした。
▼ スロットリングあり
続いてネットワークを高速4Gにスロットリングさせて比較してみます。
- 外部CSS参照パターン(左画像):0.42s
- CSSをインライン化したパターン(右画像):0.24s
外部CSS参照パターンでは、styles.cssがレンダリングのブロッカーになっています。
ネットワークを低速4Gにスロットリングさせて比較してみます。
- 外部CSS参照パターン(左画像):1.22s
- CSSをインライン化したパターン(右画像):0.64s
ネットワークを3Gにスロットリングさせて比較してみます。
- 外部CSS参照パターン(左画像):4.11s
- CSSをインライン化したパターン(右画像):2.11s
CSSをインライン化パターンでは、ファーストビューに必要なCSSがHTMLに直接埋め込まれているため、外部CSSファイルの読み込みを待つ必要がありません。よってstyles.cssがブロッカーにならずFCP、LCPの時間短縮されています。
低速なネットワーク環境で差がみられるのでモバイルユーザーの体験改善に有効かと思いました。
web.devではLCPが2.5s以内を良好なスコアとしこれに収めることを推奨しています。
- 良好な LCP スコアとは:
https://web.dev/articles/lcp?hl=ja#what-is-a-good-lcp-score
クリティカルCSSを作成するツール
クリティカルCSSを抽出するツールとして以下のようなものがあります。
- Critical Path CSS Generator
- critical
参考:https://web.dev/articles/extract-critical-css#criticalcss
参考