JavaScript エコシステム全体で Largest Contentful Paint を改善。
Google はプロジェクト Aurora の一環として、一般的なウェブ フレームワークと連携し、Core Web Vitals に準拠した優れたパフォーマンスを実現しています。Angular と Next.js では、この記事の前半で説明したフォント インライン化がすでに実装されています。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 でホストされている外部ファイルを参照していることに注意してください。アプリケーションを読み込む際、ブラウザはまずヘッドで参照されている元のスタイルシートをダウンロードする必要があります。
  次に、ブラウザは woff2 ファイルをダウンロードし、最後にアプリケーションのレンダリングを開始できます。
  最適化の機会として、初期スタイルシートをビルド時にダウンロードし、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 インラインが Angular で利用可能になり、v12 ではデフォルトで有効になりました。v11 を使用している場合は、angular.json で inlineCritical プロパティを true に設定して有効にします。Next.js でこの機能を有効にするには、next.config.js に experimental: { optimizeCss: true } を追加します。
まとめ
この記事では、Chrome とウェブ フレームワークの連携について説明しました。フレームワークの作成者で、Google が取り組んだ問題の一部が自分のテクノロジーに存在すると認識している場合は、Google の調査結果を参考に、同様のパフォーマンス最適化を適用することをおすすめします。
改善点の詳細Google が Core Web Vitals に対して実施してきた最適化作業の一覧については、Aurora のご紹介をご覧ください。