レイアウトシフトとは#
数十秒の短い動画で説明します。
より詳細な説明は、レイアウトシフトとは、ウェブページ上で突然の変化が発生した際に、ページ内のコンテンツの位置が予期せず移動する現象を指します。このような状況は、読書の中断や誤操作を引き起こすため、しばしば困惑させます。レイアウトシフトは通常、リソースの非同期読み込みや、動的にページに追加された DOM 要素によって引き起こされます。考えられる原因には、サイズが不明な画像や動画、フォントとその代替フォントのレンダリングサイズの違い、またはサードパーティの広告やウィジェットが動的にサイズを調整することが含まれます。
困ったことに、ウェブサイトの開発過程での機能は、ユーザー体験と大きく異なることがよくあります。パーソナライズされたコンテンツやサードパーティのコンテンツの動作は、開発環境と本番環境で異なることが多く、テスト画像は通常、開発者のブラウザキャッシュに存在し、ローカルで実行される API 呼び出しは非常に速く、遅延はほとんど感じられません。
CLS とは#
累積レイアウトシフト(Cumulative Layout Shift、CLS)は指標です。
ページのライフサイクル全体にわたって発生する各予期しないレイアウト変化の最大レイアウト変化スコアを測定します。
CLS は、実際のユーザーがレイアウトシフトに遭遇する頻度を測定することで、レイアウトシフトの問題を解決する手助けをします。これにより、開発者は実際のユーザーにおけるレイアウトシフトの発生状況を理解し、適切な修正措置を講じることができます。
CLS を最適化する理由#
レイアウトシフトはユーザー体験に非常に影響を与える問題であり、上記の短い動画でも理解できます。
レイアウトシフトは通常、予期しないクリックやページの方向を失わせ、最終的にはユーザーを挫折させる原因となります。ユーザーは通常、長く留まることはありません。また、時にはユーザーが予想された製品フローに従わないこともあります。
通常、レイアウトシフトを最適化することで、ユーザーの粘着性や滞在時間などの指標を向上させることができます。
Yahoo! JAPAN News は、CLS を 0.2 ポイント低下させることで、以下の成果を得ました。
CLS を低下させる方法#
画像などのメディア要素のプレースホルダー#
画像、動画などのメディアリソース要素には、常に幅と高さの属性を含めます。または、CSS のmin-height
、aspect-ratio
、または類似の方法で必要なスペースを保持します。
aspect-ratio
#
現在の要素の比率を直接指定するために使用できます。
https://developer.mozilla.org/zh-CN/docs/Web/CSS/aspect-ratio
ブラウザのサポート:
padding-bottom
#
ブラウザのサポートの問題を考慮する場合、現在広く受け入れられている基本的な解決策「Padding-Top Hack」を使用することもできます。この解決策には、親要素と絶対位置の子要素が必要です。次に、アスペクト比のパーセンテージを計算してpadding-top
に設定します。例えば:
<div class="container">
<img class="media" src="..." alt="...">
</div>
.container {
position: relative;
width: 100%;
padding-top: 56.25%; /* 16:9 アスペクト比 */
}
.media {
position: absolute;
top: 0;
}
シフトを引き起こしにくい CSS を使用する#
その中でtransform
は非常に良好に機能します。以下にいくつかの例を示します。
ユースケースはここで見つけることができます:https://play.tailwindcss.com/26PxFA6UVI
zoom
VS transform: scale
#
zoom
がページを拡大し右にシフトするのに対し、transform: scale
はその場で拡大します。
margin
VS transform: translate
#
margin
は親要素を大きくし、transform: translate
は現在の要素を移動させるだけです。
border
VS box-shadow
#
border
は親要素を押し上げますが、box-shadow
はそうではありません。
遅延読み込みに注意#
遅延読み込みはレイアウトのシフトを引き起こす可能性があります。遅延読み込みの長いリストの中で移動する場合は注意してください!
アニメーションなしで移動することで、この問題をある程度回避できます。
transition: all
の使用に注意#
ページが初めて読み込まれるか、ページが移動する際に、transition: all
は要素のpadding
などが 0 からレンダリングされる原因となり、ページの揺れを引き起こす可能性があります。
これはすべて痛いです:
コミット:テーブルおよびリンクアイコンの揺れ
コミット:ナビゲーションバーの揺れの問題を修正
タグの順序によるシフトの問題#
モバイル端末では主要なコンテンツが優先的に表示されるため、サイドバーのマークアップは主要なコンテンツの後ろに配置されます。一方、大きな画面では、CSS のorder
を設定することで順序を変更し、主要なコンテンツを中央(つまり第 2 列)に移動させます。擬似コードは以下の通りです:
export default function MainLayout(props) {
return (
<Container>
<Main className={css`@media screen and (min-width: breakpoint) { order: 0 }`} />
<Left className={css`@media screen and (min-width: breakpoint) { order: -1 }`} />
<Right className={css`@media screen and (min-width: breakpoint) { order: 1 }`} />
</Container>
)
}
ブラウザは最初の描画時に DOM を完全に解析しておらず、<Main />
の存在は知っていますが、<Left />
や<Right />
の存在は知らないため、<Main />
を第 1 列にレンダリングします。2 回目の描画時に、ブラウザは<Main />
を第 2 列にレンダリングし、<Left />
を第 1 列にレンダリングします。
Chrome は HTML を一度に完全に解析するわけではなく、以下の 2 つの状況で解析を一時停止し、レンダリングと描画を開始します:
- Chrome のパーサーが 65535 バイトの HTML を読み取った後に一時停止します。
- Chrome が
<script>
タグに遭遇すると、約 50 トークンを読み取った後に一時停止します。
詳細については、ブログの累積レイアウトシフト(CLS)問題の最適化を参照してください。
ウェブページの移動と前進後退キャッシュ#
デフォルトでは、すべてのブラウザは bfcache を使用しますが、さまざまな理由から、一部のサイトは bfcache を使用するのに適していません。bfcache の使用を妨げる問題をテストし識別する方法についての詳細は、bfcache の記事をお読みください。
離れた後、bfcache はページをブラウザのメモリに短期間保存しますので、戻ると、離れたときの状態に完全に復元されます。これにより、完全に読み込まれたページが即座に利用可能になり、変化はありません。
現在の SPA アプリケーションは、ルーティング移動時のページレイアウトの一貫性を簡単に保証できます。常にディレクトリとナビゲーションバーをページの固定位置に保つことを忘れないでください。
フォント#
ネットワークフォントのダウンロードとレンダリングの前に、通常は 2 つの処理方法があります:
- ネットワークフォントを代替フォントに置き換える(FOUT—— スタイルのないテキストのちらつき)。
- ネットワークフォントが利用可能になるまで、代替フォントで「不可視」テキストを表示する(FOIT—— 不可視テキストのちらつき)。
これらの 2 つの方法は、レイアウトの変化を引き起こす可能性があります。テキストが不可視であっても、代替フォントを使用してレイアウトが行われます。つまり、そのフォントを使用しているテキストブロックと周囲のコンテンツは、ネットワークフォントが読み込まれるときにレイアウトが変化し、FOUT の可視フォントと全く同じです。
以下の方法は、この問題を最小限に抑えるのに役立ちます:
font-display: optional
を使用すると、初期レイアウト時にネットワークフォントが利用可能な場合にのみ使用されるため、再レイアウトを回避できます。- マッチング度の高い代替フォントを使用します。例えば、
font-family: "Google Sans", sans-serif;
を使用すると、"Google Sans" フォントが読み込まれる際にブラウザのサンセリフ代替フォントが使用されます。font-family: "Google Sans"
を使用して代替フォントを指定しない場合、デフォルトフォントが使用され、Chrome ではデフォルトフォントが "Times" であり、デフォルトのサンセリフフォントよりもマッチング度が低くなります。 - 新しい
size-adjust
、ascent-override
、descent-override
、およびline-gap-override
API を使用して、代替フォントとネットワークフォントのサイズ差を最小限に抑えます。詳細については、「改善されたフォントフォールバック」の記事を参照してください。 - Font Loading APIを使用して、必要なフォントを取得する時間を短縮します。
<link rel=preload>
を使用して、重要なネットワークフォントを早期に読み込みます。プリロードされたフォントは、初回描画に達する可能性が高く、レイアウトの変化が発生しません。- フォントのベストプラクティスに関する「フォントのベストプラクティス」の記事をお読みください。
本物のスケルトンスクリーンを使用する#
CLS スコアの測定#
本番段階#
- Chrome ユーザーエクスペリエンスレポート
- PageSpeed Insights
- Search Console(Core Web Vitals レポート)
- web-vitals JavaScript ライブラリ
実験段階#
DevTools の Lighthouse#
モバイルデバイスとデスクトップデバイスのウェブページの実際のパフォーマンスレポートを生成し、対応するウェブページを改善するための提案を提供できます。
ローカル開発中に DevTools から Lighthouse を実行するのは非常に便利です。
PageSpeed Insights#
おそらくオンライン版の Lighthouse です。
DevTools のパフォーマンス#
パフォーマンスタブは、Chrome の DevTools プロファイルのすべてのページの動作を一定期間記録します。タイムラインには「Experience」とラベル付けされたレイヤーが表示され、レイアウトの変化と変化した要素が強調表示されます。
Web Vitals 拡張機能#
Web Vitals 拡張機能は、パフォーマンスの問題を見つけるための抜き打ちツールとして考えるのが最適であり、包括的なデバッグツールではありません —— これは Chrome の DevTools 内のパフォーマンスタブの仕事です。
結論#
自分のプロジェクトに高い要求を持つ人として、普段はレイアウトシフトの最適化や Lighthouse に触れることが多いですが、以前は自分が試行錯誤していたときに CLS という概念はありませんでした。今では CLS について比較的明確な概念を持つようになりました。
CLS は非常に基本的な最適化指標であり、ユーザー体験にとって非常に重要です。どのプロジェクトも CLS に対して最適化を行うべきです。
誤りがあれば、速やかに指摘してください。ありがとうございます!
参考#
- https://web.dev/cls/
- https://web.dev/optimize-cls
- https://developers.google.com/publisher-tag/guides/minimize-layout-shift
- https://web.dev/yahoo-japan-news/
- https://addyosmani.com/blog/infinite-scroll-without-layout-shifts/
- https://blog.skk.moe/post/fix-blog-cls/
- https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio