🛠️

Next.js学習:最適化編|next/fontによるフォント最適化の何が優れているのか?

    Next.js学習記録|なんとなく理解禁止シリーズ
    Next.js

Next.jsのApp Routerではnext/fontを使ったフォント最適化が可能で、ざっと下記のような恩恵を得られます。

  • Webフォントのセルフホスティングによるパフォーマンス向上
  • Webフォントのレイアウトシフトを解消できる
  • フォントがサブセット化されるので軽量化できる

上記のメリットを公式ドキュメントを参照しつつまとめた後、最後にフォント最適化方法とセルフホスティングする際のアップロード時間等にも触れていきます!

【恩恵1/3】Webフォントのセルフホスティングによるパフォーマンス向上

まずは公式ドキュメントを見てみます。

next/font will automatically optimize your fonts (including custom fonts) and remove external network requests for improved privacy and performance.
Automatically self-host any Google Font. Fonts are included in the deployment and served from the same domain as your deployment. No requests are sent to Google by the browser.
Font Optimization|Next.js App Router

要するに、next/fontに組み込まれている自動セルフホスティングによって、ページ読み込み時にブラウザからGoogleにWebフォントのリクエストを送る必要がなく、ページのレンダリング時にWebフォントも綺麗に表示できてパフォーマンスが良いということです。

ちなみにフォントのセルフホストについては、下記が参考になります!Google Fontsは全てのフォントが無料で配布されているからセルフホストが可能で、それをnext/fontが活用してビルドの処理に組み込んでくれているイメージ。

デバイスフォントとWEBフォントの使い分けについて|note

【恩恵2/3】Webフォントのレイアウトシフトを解消できる

レイアウトシフトとは、画像やWebフォントがページのレンダリングより遅れて読み込まれることによる、レイアウトの変化(ちらつく現象)です。

【CSS】画像のレイアウトシフト(CLS)を防ぐ|Zenn

セルフホストせず普通にブラウザからWebフォントのリクエストを送ると、一度デフォルトのフォントスタイルがレンダリングされてから、遅れてWebフォントが適用されることになります。

ユーザーの体験も損ねるし、何よりもデフォルトフォントよりも行間や字間が異なれば、要素の高さにも影響を与えます。

next/fontを使ってセルフホストすれば、上記のようなレイアウトシフトを起こすことなく、綺麗に表示ができます。

next/font includes built-in automatic self-hosting for any font file. This means you can optimally load web fonts with zero layout shift, thanks to the underlying CSS size-adjust property used.
Font Optimization|Next.js App Router

【恩恵3/3】フォントがサブセット化されるので軽量化できる

サブセット化とは、成果物に必要な文字だけを抜き出して配信する技術のことで、不要な文字をインストールしない分、サイズが軽量化できます。

サブセット化とは何ですか?|Font Plus

英語なら全てのアルファベットをインストールすれば良いですが、日本語はなんせ漢字という強敵がいるので、全てインストールするのとしないとでは、パフォーマンスに差が出てくるみたいです。

Google Fontsは自動でサブセット化をしてくれるようで、サブセット化されたフォントデータをnext/fontでセルフホストすることで、より一層パフォーマンスが良くなります。

Google Fonts are automatically subset. This reduces the size of the font file and improves performance. You'll need to define which of these subsets you want to preload. Failing to specify any subsets while preload is true will result in a warning.
Font Optimization - Specifying a subset|Next.js App Router

Next.jsでフォント最適化を行う方法

例えば、InterというGoogle Fontsを組み込む場合は、下記のような記述になります。

import { Inter } from 'next/font/google'
 
// If loading a variable font, you don't need to specify the font weight
const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
})
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

Inter関数を通して返してもらったオブジェクトの.classNameプロパティにアクセスすれば、レンダリング時に下記のようなスタイルが付与されます。

.__className_1b1067 {
    font-family: '__Zen_Kaku_Gothic_New_1b1067', '__Zen_Kaku_Gothic_New_Fallback_1b1067';
    font-weight: 400;
    font-style: normal;
}

全てのGoogle Fontsの太さやその他のプロパティを参照したい場合は、node_modules/next/dist/compiled/@next/font/dist/google/index.d.tsの定義ファイルを閲覧してみてください。

セルフホスティングをしても自動デプロイのランタイム等に影響は出ないか?

GitHubのワークフローを使って自動デプロイをされる方も多いと思いますが、FTPにアップロードする際に、同じファイルはスルーしてくれるので、特にワークフローの所要時間に影響はありません。

⚖️  File content is the same, doing nothing: _next/static/media/fce9d82870a6554e-s.woff2
⚖️  File content is the same, doing nothing: _next/static/media/fd4b0a8b453e0408-s.woff2
⚖️  File content is the same, doing nothing: _next/static/media/fd4bcb34bc8350cc-s.woff2
⚖️  File content is the same, doing nothing: _next/static/media/fd5aa9ec8fb4b324-s.woff2
⚖️  File content is the same, doing nothing: _next/static/media/fdb7007665dfe7e1-s.woff2
⚖️  File content is the same, doing nothing: _next/static/media/fdc51fe54f7a18d7-s.woff2
⚖️  File content is the same, doing nothing: _next/static/media/fddba5c9a27852ad-s.woff2

このような形でdoing nothingとある通りスルーをしていて、_next/static/media/fddba5c9a27852ad-s.woff2このハッシュ値の箇所も、特にフォントの読み込みに変更を加えない限りは差分が出ません。

ちなみに初回デプロイ時は、このセルフホストのおかげで合計11m30sも掛かりましたが、2回目以降はキャッシュのおかげで1m40sくらいに落ち着いています。