JavaScript フレームワークでのリソースのインライン化

JavaScript エコシステム全体で Largest Contentful Paint を改善。

Google はプロジェクト Aurora の一環として、一般的なウェブ フレームワークと連携し、Core Web Vitals に準拠した優れたパフォーマンスを実現しています。Angular と Next.js では、すでにフォントをインライン化できます。これについては、この記事の第 1 部で説明しています。2 つ目の最適化は、クリティカル CSS インラインです。これは Angular CLI でデフォルトで有効になっており、Nuxt.js では実装中です。

フォントのインライン化

数百のアプリを分析した結果、Aurora チームは、デベロッパーが index.html<head> 要素でフォントを参照してアプリにフォントを組み込むことが多いことを発見しました。マテリアル アイコンを含めた場合の例を次に示します。

<!doctype html>
<html lang="en">
<head>
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  ...
</html>

このパターンは完全に有効で機能しますが、アプリのレンダリングがブロックされ、追加のリクエストが発生します。何が起こっているかを詳しく理解するには、上の HTML で参照されているスタイルシートのソースコードをご覧ください。

/* fallback */
@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}

.material-icons {
  /*...*/
}

font-face 定義が fonts.gstatic.com でホストされている外部ファイルを参照していることに注意してください。ブラウザは、アプリケーションを読み込むときに、head で参照されている元のスタイルシートをダウンロードする必要があります。

ウェブサイトからサーバーにリクエストを行い、外部スタイルシートをダウンロードする方法を示す画像
まず、ウェブサイトがフォント スタイルシートを読み込みます。

次に、ブラウザが woff2 ファイルをダウンロードし、最後にアプリケーションのレンダリングを開始できます。

フォント スタイルシート用とフォント ファイル用の 2 つのリクエストを示す画像。
次に、フォントを読み込むリクエストが送信されます。

最適化の機会として、初期スタイルシートをビルド時にダウンロードし、index.html にインライン化できます。これにより、実行時に CDN へのラウンド トリップ全体がスキップされ、ブロック時間が短縮されます。

アプリケーションをビルドすると、リクエストが CDN に送信され、スタイルシートが取得され、HTML ファイルにインライン化され、<link rel=preconnect> がドメインに追加されます。この手法を使用すると、次のような結果が得られます。

<!doctype html>
<html lang="en">
<head>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
  <style type="text/css">
  @font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
  ...
</html>

Next.js と Angular でフォントのインライン化が可能に

フレームワークのデベロッパーが基盤となるツールに最適化を実装すると、既存のアプリケーションと新しいアプリケーションで最適化を簡単に有効にでき、エコシステム全体の改善につながります。

この改善は、Next.js v10.2 と Angular v11 からはデフォルトで有効になっています。どちらも Google フォントと Adobe フォントの インライン化をサポートしていますAngular では、後者が v12.2 で導入される予定です。

Next.js でのフォント インライン化の実装と、Angular のコンテキストでこの最適化について説明する動画をご覧ください。

重要な CSS をインライン化する

また、重要な CSS をインライン化することで、First Contentful Paint(FCP)Largest Contentful Paint(LCP)の指標を改善しました。ページの重要な CSS には、最初のレンダリングで使用されるすべてのスタイルが含まれます。トピックの詳細については、クリティカルでない CSS を先送りするをご覧ください。

多くのアプリがスタイルを同期的に読み込み、そのためアプリのレンダリングがブロックされています。簡単な解決策は、スタイルを非同期で読み込むことです。media="all" でスクリプトを読み込むのではなく、media 属性の値を print に設定し、読み込みが完了したら、属性値を all に置き換えます。

<link rel="stylesheet" href="..." media="print" onload="this.media='all'">

ただし、この方法ではスタイル設定されていないコンテンツがちらつく可能性があります。

スタイルが読み込まれるとページがちらつくように見える。

上の動画は、スタイルを非同期で読み込むページのレンダリングを示しています。フリッカーが発生するのは、ブラウザが最初にスタイルのダウンロードを開始し、次に HTML をレンダリングするためです。ブラウザがスタイルをダウンロードすると、リンク要素の onload イベントがトリガーされ、media 属性が all に更新され、スタイルが DOM に適用されます。

HTML のレンダリングとスタイルの適用の間に、ページの一部はスタイルが適用されません。ブラウザがスタイルを使用すると、ちらつきが発生します。これはユーザー エクスペリエンスの低下を招き、累積レイアウト シフト(CLS)の低下を招きます。

クリティカル CSS のインライン化と非同期スタイル読み込みにより、読み込み動作を改善できます。Critters ツールは、スタイルシート内のセレクタを確認し、HTML と照合することで、ページで使用されているスタイルを検出します。一致が見つかった場合、対応するスタイルは重要な CSS の一部と見なされ、インライン化されます。

例を見てみましょう。

すべきでないこと
<head>
   <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>
/* styles.css */
section button.primary {
  /* ... */
}
.list {
  /* ... */
}

インライン化前の例。

上記の例では、クリッターは styles.css のコンテンツを読み取って解析し、その後、2 つのセレクタを HTML と照合して、section button.primary が使用されていることを検出します。最後に、Critters は、ページの <head> に対応するスタイルをインライン化します。

すべきこと
<head>
  <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
  <style>
  section button.primary {
    /* ... */
  }
  </style>
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>

インライン化後の例。

クリティカルな CSS を HTML にインライン化すると、ページのちらつきが解消されます。

CSS インライン化後のページ読み込み。

クリティカル CSS インラインが Angular で利用可能になり、v12 ではデフォルトで有効になりました。v11 を使用している場合は、angular.jsoninlineCritical プロパティを true に設定して有効にします。Next.js でこの機能を有効にするには、next.config.jsexperimental: { optimizeCss: true } を追加します。

まとめ

この投稿では、Chrome とウェブ フレームワークのコラボレーションについていくつか触れました。フレームワークの作成者で、Google が取り組んだ問題の一部が自分のテクノロジーに存在すると認識している場合は、Google の調査結果を参考に、同様のパフォーマンス最適化を適用することをおすすめします。

改善点の詳細Google が Core Web Vitals に対して行った最適化作業の包括的なリストについては、Aurora の紹介の投稿をご覧ください。