WebFont の読み込みとレンダリングを最適化する

すべてのスタイル バリエーション(必要でない可能性もあります)とすべてのグリフ(使用されない可能性もあります)を含む「完全な」WebFont では、数メガバイトのダウンロードが必要になる可能性があります。この記事では、訪問者が使用するフォントのみダウンロードされるように、WebFonts の読み込みを最適化する方法について説明します。

すべてのバリエーションを含む大きなファイルの問題に対処するため、@font-face CSS ルールは、フォント ファミリーをリソースのコレクションに分割できるように特別に設計されています。たとえば、Unicode のサブセットや、異なるスタイルのバリエーションなどです。

これらの宣言に基づいて、ブラウザは必要なサブセットとバリエーションを特定し、テキストのレンダリングに必要な最小セットをダウンロードします。これは非常に便利です。ただし、注意しないと、クリティカルなレンダリング パスでパフォーマンスのボトルネックが発生し、テキストのレンダリングが遅れることもあります。

デフォルトの動作

フォントの遅延読み込みには、テキストのレンダリングが遅れる可能性があるという重要な隠れた意味合いがあります。ブラウザは、テキストのレンダリングに必要なフォント リソースを把握する前に、DOM ツリーと CSSOM ツリーに依存するレンダリング ツリーを構築する必要があります。その結果、フォント リクエストは他の重要なリソースよりもかなり遅れ、リソースが取得されるまでブラウザがテキストのレンダリングをブロックする可能性があります。

フォントのクリティカル レンダリング パス

  1. ブラウザが HTML ドキュメントをリクエストします。
  2. ブラウザは HTML レスポンスの解析と DOM の作成を開始します。
  3. ブラウザが CSS、JS、その他のリソースを検出してリクエストをディスパッチします。
  4. ブラウザは、すべての CSS コンテンツを受信した後に CSSOM を構築し、それを DOM ツリーと組み合わせてレンダリング ツリーを構築します。
    • フォント リクエストは、レンダリング ツリーがページ上の指定されたテキストのレンダリングに必要なフォント バリエーションを示した後にディスパッチされます。
  5. ブラウザはレイアウトを実行し、コンテンツを画面に描画します。
    • フォントがまだ利用できない場合は、ブラウザでテキスト ピクセルがレンダリングされないことがあります。
    • フォントが使用可能になると、ブラウザはテキスト ピクセルをペイントします。

ページ コンテンツの最初のペイント(レンダリング ツリーが構築された直後に実行される場合があります)とフォント リソースのリクエストの間の「競合」が、「空白のテキストの問題」を引き起こします。この問題では、ブラウザはページ レイアウトをレンダリングしますが、テキストは省略されます。

WebFonts をプリロードし、font-display を使用して利用できないフォントでのブラウザの動作を制御することで、フォント読み込みによる空白ページやレイアウト シフトを防ぐことができます。

WebFont リソースをプリロードする

事前にわかっている URL でホストされている特定の WebFont がページで必要になる可能性が高い場合は、リソースの優先順位付けを利用できます。<link rel="preload"> を使用すると、CSSOM の作成を待たずに、クリティカル レンダリング パスの早い段階で WebFont のリクエストがトリガーされます。

テキスト レンダリングの遅延をカスタマイズする

プリロードすると、ページのコンテンツがレンダリングされるときに WebFont が利用可能になる可能性が高くなりますが、保証されるわけではありません。まだ利用できない font-family を使用したテキストをレンダリングする際のブラウザの動作も考慮する必要があります。

フォントの読み込み中にテキストが見えないようにするという投稿では、デフォルトのブラウザの動作が一貫していないことがわかります。ただし、font-display を使用して、動作を指定できます。

Browser Support

  • Chrome: 60.
  • Edge: 79.
  • Firefox: 58.
  • Safari: 11.1.

Source

一部のブラウザで実装されている既存のフォント タイムアウト動作と同様に、font-display はフォント ダウンロードの存続期間を次の 3 つの主要な期間に分割します。

  1. 最初の期間はフォント ブロック期間です。この期間中、フォントフェイスが読み込まれていない場合、そのフォントフェイスを使用する要素は、代わりに不可視のフォールバック フォントフェイスでレンダリングする必要があります。ブロック期間中にフォントフェイスが正常に読み込まれると、そのフォントフェイスは通常どおり使用されます。
  2. フォント スワップ期間は、フォント ブロック期間の直後に発生します。この期間、フォントフェイスが読み込まれていない場合、そのフォントフェイスを使用する要素は、代わりにフォールバック フォントフェイスでレンダリングする必要があります。スワップ期間中にフォントフェイスが正常に読み込まれると、そのフォントフェイスが通常どおり使用されます。
  3. フォント障害期間は、フォント スワップ期間の直後に発生します。この期間の開始時にフォントフェイスがまだ読み込まれていない場合、読み込みに失敗したとしてマークされ、通常のフォント フォールバックが発生します。それ以外の場合は、フォントフェイスが通常どおり使用されます。

これらの期間を理解することで、font-display を使用して、フォントがダウンロードされたかどうかやダウンロードされたタイミングに応じて、フォントのレンダリング方法を決定できます。

font-display プロパティを使用するには、@font-face ルールに追加します。

@font-face {
  font-family: 'Awesome Font';
  font-style: normal;
  font-weight: 400;
  font-display: auto; /* or block, swap, fallback, optional */
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range: U+000-5FF; /* Latin glyphs */
}

font-display は現在、次の値の範囲をサポートしています。

  • auto
  • block
  • swap
  • fallback
  • optional

フォントのプリロードと font-display プロパティの詳細については、次の投稿をご覧ください。

Font Loading API

<link rel="preload"> と CSS font-display を併用すると、オーバーヘッドを増やすことなく、フォント読み込みとレンダリングを細かく制御できます。ただし、追加のカスタマイズが必要な場合や、JavaScript の実行によって発生するオーバーヘッドを許容できる場合は、別の方法があります。

Font Loading API は、CSS フォントフェイスを定義して操作し、ダウンロードの進行状況を追跡し、デフォルトの遅延読み込み動作をオーバーライドするためのスクリプト インターフェースを提供します。たとえば、特定のフォント バリアントが必要な場合は、それを定義して、フォント リソースの即時取得を開始するようブラウザに指示できます。

Browser Support

  • Chrome: 35.
  • Edge: 79.
  • Firefox: 41.
  • Safari: 10.

Source

var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
  style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
});

// don't wait for the render tree, initiate an immediate fetch!
font.load().then(function() {
  // apply the font (which may re-render text and cause a page reflow)
  // after the font has finished downloading
  document.fonts.add(font);
  document.body.style.fontFamily = "Awesome Font, serif";

  // OR... by default the content is hidden,
  // and it's rendered after the font is available
  var content = document.getElementById("content");
  content.style.visibility = "visible";

  // OR... apply your own render strategy here...
});

また、フォント ステータス(check() メソッドを介して)を確認してダウンロードの進行状況を追跡できるため、ページにテキストをレンダリングするためのカスタム戦略を定義することもできます。

  • フォントが利用可能になるまで、すべてのテキストのレンダリングを保留できます。
  • フォントごとにカスタム タイムアウトを実装できます。
  • 代替フォントを使用すると、レンダリングをブロック解除し、フォントが使用可能になった後に目的のフォントを使用する新しいスタイルを挿入できます。

ページ上のさまざまなコンテンツに合わせて、上記の戦略を組み合わせて使用することもできます。たとえば、フォントが使用可能になるまで一部のセクションのテキストのレンダリングを遅らせ、フォールバック フォントを使用して、フォント ダウンロードの完了後に再レンダリングできます。

適切なキャッシュが必要です

フォント リソースは通常、頻繁に更新されない静的リソースです。そのため、長い max-age の有効期限に適しています。すべてのフォント リソースに、条件付き ETag ヘッダー最適な Cache-Control ポリシーの両方を指定してください。

ウェブ アプリケーションがサービス ワーカーを使用している場合、ほとんどのユースケースでは、キャッシュファースト戦略でフォント リソースを提供する方法が適しています。

localStorage または IndexedDB を使用してフォントを保存しないでください。それぞれに独自のパフォーマンスの問題があります。ブラウザの HTTP キャッシュは、ブラウザにフォント リソースを配信するための最適で最も堅牢なメカニズムです。

WebFont の読み込みチェックリスト

  • <link rel="preload">font-display、または Font Loading API を使用してフォントの読み込みとレンダリングをカスタマイズする: デフォルトの遅延読み込み動作では、テキストのレンダリングが遅れる可能性があります。これらのウェブ プラットフォーム機能を使用すると、特定のフォントに対してこの動作をオーバーライドしたり、ページ上のさまざまなコンテンツにカスタム レンダリングとタイムアウト戦略を指定したりできます。
  • 再検証と最適なキャッシュ ポリシーを指定します。フォントは、更新頻度の低い静的リソースです。サーバーから長期間有効な max-age タイムスタンプと再検証トークンが提供され、異なるページ間でフォントを効率的に再利用できるようにします。サービス ワーカーを使用する場合は、キャッシュファーストの戦略が適しています。

Lighthouse による WebFont の読み込み動作の自動テスト

Lighthouse を使用すると、ウェブフォント最適化のベスト プラクティスに沿ってサイトを構築するためのプロセスを自動化できます。

以下の監査を行うと、ページがウェブフォント最適化のベスト プラクティスに沿って継続的に作成されていることを確認できます。