レイアウトシフトとは#
数十秒の短い動画で説明します。
より詳細な説明は、レイアウトシフトとは、ウェブページ上で突然の変化が発生した際に、ページ内のコンテンツの位置が予期せず移動する現象を指します。このような状況は、読書の中断や誤操作を引き起こすため、しばしば困惑させます。レイアウトシフトは通常、リソースの非同期読み込みや、動的にページに追加された 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
を設定することで順序を変更し、主要コンテンツを中央(つまり第二列)に移動させます。擬似コードは以下の通りです:
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 />
を第一列にレンダリングします。二回目の描画時に、ブラウザは<Main />
を第二列にレンダリングし、<Left />
を第一列にレンダリングします。
Chrome は HTML を一度に完全に解析するわけではなく、以下の二つの状況で解析を一時停止し、レンダリングと描画を開始します:
- Chrome のパーサーは 65535 バイトの HTML を読み取った後に一時停止します。
- Chrome は
<script>
タグに遭遇すると、約 50 個の「トークン」を読み取った後に一時停止します。
詳細については、ブログの累積レイアウトシフト(CLS)問題の最適化を参照してください。
ページ遷移と前進後退キャッシュ#
デフォルトでは、すべてのブラウザは bfcache を使用しますが、さまざまな理由から、一部のサイトは bfcache の使用に適していません。bfcache の使用を妨げる問題をテストし、特定する方法についての詳細は、bfcache の記事をお読みください。
離れた後、bfcache はページをブラウザのメモリに短時間保存しますので、戻ると、離れたときの状態に完全に復元されます。これにより、完全に読み込まれたページが即座に利用可能になり、変化が発生しません。
現在の SPA アプリケーションは、ルーティング遷移ページのレイアウトの一貫性を簡単に保証できます。常にディレクトリとナビゲーションバーをページの固定位置に保つことを忘れないでください。
フォント#
ネットワークフォントをダウンロードしてレンダリングする前に、通常は二つの処理方法があります:
- ネットワークフォントを代替フォントの代わりに使用する(FOUT—— スタイルのないテキストのちらつき)。
- ネットワークフォントが利用可能でテキストが表示されるまで、代替フォントで「見えない」テキストを表示する(FOIT—— 見えないテキストのちらつき)。
これら二つの方法は、レイアウトの変化を引き起こす可能性があります。たとえテキストが見えなくても、代替フォントがレイアウトに使用されます。これは、ネットワークフォントが読み込まれるときに、そのフォントを使用するテキストブロックや周囲のコンテンツがレイアウトの変化を引き起こすことを意味します。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