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
ファイルをダウンロードし、最後にアプリケーションのレンダリングを開始できます。
最適化の機会として、初期スタイルシートをビルド時にダウンロードし、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 の紹介の投稿をご覧ください。