📌

【パフォーマンス】Next.js から学ぶCSSのインライン化

3 min read

こんにちは、@kaa_a_zu です。Zennにおける処女作であるこの記事では、スタイルをCSSファイルとして定義し読み込むのではなく、インライン化することが何故パフォーマンスに影響を及ぼすのか、またインライン化を行うときに何に注意をしないといけないのかについて書こうと思う。

インラインCSS の効果

まず、「CSSをインライン化することでパフォーマンスが改善される」ことが分かる事象について紹介する。

Next.js の automatic-webfont-optimization

Reactのフレームワークである Next.js v10.2 で導入された automatic-webfont-optimization が機能したことで、WebFontを利用しているサイトでは、以下のようなパフォーマンス改善が(ほぼ)起こる (※LightHouseでのスコアが全てではないが、パフォーマンスが向上したことがわかる)

Before:
webFont のインライン化前

After:
webFont のインライン化後

計測されているサイトでは、WebFontとして Noto Sans JP が利用されており、このFontCSSが上述したNext.jsの機能によってCSSファイルとしての読み取りではなく、インライン化されたため、このような改善が起こった。(FCP, TTLなどの指標も全体的に短くなった)

こちらの記事でも同様のパフォーマンス改善が報告されている

なぜCSSのインライン化をするのか

まず、前提知識としてブラウザがコンテンツを表示するための行程と、レンダリングブロックについて知る必要がある。ブラウザがコンテンツを表示するための行程は大まかに以下の通りである。(尚、すごく抽象化している)

  1. HTMLのダウンロード
  2. HTMLをパースしてDOMの生成
  3. 2の最中に外部リソースの読み取りがあるときは読み取りを行う

次にレンダリングブロックについて。上述した3における外部リソースに該当するものには、画像, CSS, JSファイルなどがある。このうち、CSSとJSはレンダリングをブロックしてしまう。具体的には <link rel = "style.css" href = "style.css"> というタグをHTMLパース時に見つけたら、以下を行う。

  1. style.css をダウンロード
  2. CSSOMの生成

そして、この最中はレンダリングがブロックされる。

既に察している方もいると思うが、

なぜCSSのインライン化をするのか

の回答としては、レンダリングのブロックを防ぎコンテンツの表示を早くするためである。より詳細なレンダリングブロックについてはGoogleの記事を見ていただきたい。

CSSをインライン化する時に注意すること

「では、全てのCSSをインラインにすれば良いのでは🤔」と思われるかもしれないが、そういう訳にはいかない。それはDOMの肥大化が発生するためだ。具体的に <p style="font-size: 20px; color: red;">CSSのインライン化</p> のようにDOMそれぞれにスタイル定義をしていくと、想像通りにリソースサイズが大きくなってしまう。これは、HTML自体のダウンロード速度の低下につながる。次の記事では全てのDOMそれぞれにスタイル定義をした場合と、CSSファイルを読み取った場合のパフォーマンスについて書かれている。DOMサイズ肥大化のベンチマーク

では、どうすれば良いのかというと、別のインライン化の方法を試すべきである。これを実際に行ったのが冒頭で紹介したNext.jsのWebFont最適化である。これは具体的に、以下のような最適化(インライン化)を行っている。

Before:

<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap" 
      rel="stylesheet"
/>

After:

<head>
	<style data-href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&amp;display=swap">
		@font-face{font-family:'Noto Sans JP';font-style:normal;....
	</style>
</head>

(フォントなので若干分かりにくいが) Afterの結果において、DOMそれぞれにフォントスタイルを定義するのではなくて、全体で定義をしていることが見てとれる。これを応用すればフォント以外の普通のスタイル定義にも使えることが理解できる。 この時に注意しなくてはいけないのは 「DOMそれぞれにスタイルを定義した時に起こる問題を防ぐために」と同様の理由で、必要最低限のインライン化をすること だ。例えば、ユーザーのファーストビューに必要なスタイルだけ<style> タグを使ってインライン化すると、リソースサイズの肥大化を最小限に抑えながら、かつより良いパフォーマンスの改善ができる。方法としては以下に示す通りだ。

<head>
  <style>
body{background:#fff;color:#000;margin:0}.link{color:#1a0dab}.link{border-collapse:collapse}.list items{padding:0}.item{color:#000}....
  </style>
</head>

まとめ

実際にCSSをインライン化する事によってパフォーマンスに影響が及ぶ理由、何に注意すれば良いのかを理解していただけたと思う。最も、CSS周りについての最適化には ①使っていないスタイル定義を減らす ②優先度が高いCSSファイルを別にして先に読み取る ③圧縮する などもあるため併せて考えるべきだと思う。

見てくださり、ありがとうございました。