なぜ調べ始めたか
コンテンツを増やしていく中で、そもそもサイトのパフォーマンスは大丈夫なのかを確認したくなった。PageSpeed Insightsでquratedlab.comを計測したところ、デスクトップで85点、モバイルで79点という結果だった。
数字だけ見ると悪くないように思えるが、中身を見るとLCP(Largest Contentful Paint)がモバイルで5.6秒という深刻な値が出ていた。LCPは「ページのメインコンテンツが表示されるまでの時間」で、Googleが重視するCore Web Vitalsの一つだ。2.5秒以内が「良い」とされる中で5.6秒は3倍以上かかっている。
何が原因だったか
Lighthouseのレポートを詳しく見ていくと、主な原因が2つ見えてきた。
1. Google Fontsへの外部リクエスト
サイトではShippori Mincho(日本語書体)、Space Mono(等幅フォント)、Cormorant Garamond(見出し欧文書体)の3つを使っている。いずれもGoogle Fontsから読み込んでいた。
Google Fontsを使う場合の流れはこうだ。
- ブラウザがGoogleのサーバーにDNSクエリを送る
- TCP接続を確立する
- フォントのCSSファイルを取得する(89KB)
- CSSに書かれたフォントファイルのURLをもとに実際のフォントを取得する
外部ドメインへのリクエストが直列で発生するため、ネットワーク環境が悪いほど遅延が積み重なる。モバイル回線では特に影響が大きい。
2. CSSのレンダリングブロック
Astroが生成するCSSファイル(21KB〜28KB)がレンダリングをブロックしていた。モバイル計測ではこれだけで1,049ms + 722msのブロックが発生していた。
今回はまず影響の大きいフォントの問題から対処することにした。
fontsourceとは
fontsourceは、Google Fontsなどのフォントをnpmパッケージとして提供するプロジェクトだ。npm installでインストールすると、フォントファイルがローカルに配置される。Astroではimportするだけでビルド時に静的ファイルとして出力される。
外部ドメインへのリクエストがなくなるため、DNS解決やTCP接続のオーバーヘッドが消える。Cloudflare PagesのCDNから配信されるため、ユーザーの近くのエッジから届く。
実際にやったこと
1. パッケージのインストール
pnpm add @fontsource/shippori-mincho @fontsource/space-mono
Cormorant Garamondはfontsourceに存在しないため、Google Fontsのままにした。ただし欧文フォントはファイルサイズが小さく(数十KB)、日本語フォントと比べると影響は軽微だ。
2. BaseLayout.astroへのimport追加
---
import '@fontsource/shippori-mincho/japanese-400.css';
import '@fontsource/shippori-mincho/japanese-500.css';
import '@fontsource/shippori-mincho/japanese-600.css';
import '@fontsource/space-mono/400.css';
import '@fontsource/space-mono/700.css';
---
japanese-400.cssのようにjapanese-プレフィックスをつけると、日本語グリフのみを含むサブセットが使われる。font-display: swapもfontsourceのCSSにデフォルトで設定されているため、フォント読み込み中はシステムフォントで表示され、ページ表示自体がブロックされない。
3. Google FontsのURLからShippori MinchoとSpace Monoを削除
変更前のURLはこうなっていた。
family=Cormorant+Garamond:ital,wght@0,300;0,400;1,300;1,400
&family=Shippori+Mincho:wght@400;500;600
&family=Space+Mono:wght@400;700
Shippori MinchoとSpace Monoをfontsourceに移したので、Google FontsのURLからこの2つを除いた。
family=Cormorant+Garamond:ital,wght@0,300;0,400;1,300;1,400
外部リクエスト先がGoogleのサーバー1ヶ所に減る。
考え方の整理
ファイルサイズより接続コストが問題だった
Shippori Minchoのwoff2ファイルは1件あたり1.3〜1.4MBある。3ウェイト合計で約4MB。これを初回に配信するのか、と思うかもしれない。
ただし比較すべき対象は「ファイルサイズの絶対値」ではなく「ページ表示までの所要時間」だ。
Google Fontsからの読み込みでは、外部ドメインへの接続確立だけで数百msかかることがある。セルフホストではその接続コストがゼロになり、CDNキャッシュが効いた状態では2回目以降のページ表示でフォントのダウンロードは発生しない。
完全に解決したわけではない
今回の対処でGoogle Fontsへのリクエストを大幅に削減した。ただし残課題もある。
CSSのレンダリングブロック(約1,700ms相当)は改善していない。Critical CSS(ファーストビューに必要なスタイルだけをHTMLに直接埋め込む手法)で対処できるが、Astroの現行構成ではいくつか追加の設定が必要だ。
GA4のスクリプト(157KB)も読み込み時に影響がある。Partytownを使ってWeb Workerに処理を移すことで改善できるが、これも別途対応が必要になる。
改善前の計測値
| 指標 | モバイル | デスクトップ |
|---|---|---|
| パフォーマンススコア | 79 | 85 |
| LCP | 5.6s | 11.1s ※ |
| FCP | 1.5s | 10.5s ※ |
| TBT | 90ms | 0ms |
| CLS | 0.008 | 0.003 |
※ Lighthouseをheadlessで計測した値。PageSpeed InsightsのUI上の値とは異なる。
改善後の計測はデプロイ後にPageSpeed Insightsで確認する。数値が出たら続編で報告する。
サイトのパフォーマンスは、コンテンツを増やすほど「後で直す」が難しくなる。対処できるボトルネックから順番に潰しておくのが基本だと思っている。