DOM サイズが大きくなると、予想以上にインタラクティブ性に影響が及びます。このガイドでは、その理由と対処方法について説明します。
これを回避する方法はありません。ウェブページを作成すると、そのページにドキュメント オブジェクト モデル(DOM)が設定されます。DOM はページの HTML 構造を表し、JavaScript と CSS がページの構造とコンテンツにアクセスできるようにします。
ただし問題は、DOM のサイズによって、ページを迅速かつ効率的にレンダリングするブラウザの能力に影響することです。一般的に、DOM が大きいほど、最初にページをレンダリングし、その後ページのライフサイクルでレンダリングを更新するコストが高くなります。
この問題は、DOM が非常に大きいページで問題になります。DOM を変更または更新する操作によって高価なレイアウト作業が発生し、ページの迅速な応答に影響が及ぶ場合です。負荷の高いレイアウト作業は、ページの Interaction to Next Paint(INP) に影響を与える可能性があります。ページがユーザーの操作に素早く反応するには、DOM のサイズを必要以上に大きくすることが重要です。
ページの DOM が大きすぎる場合
Lighthouse によると、1,400 ノードを超えると、ページの DOM サイズが大きすぎます。ページの DOM が 800 ノードを超えると、警告がスローされるようになります。たとえば、次の HTML があるとします。
<ul>
<li>List item one.</li>
<li>List item two.</li>
<li>List item three.</li>
</ul>
上記のコードには、4 つの DOM 要素(<ul>
要素とその 3 つの <li>
子要素)があります。ウェブページにはおそらく、これよりも多くのノードがあります。そのため、DOM サイズを抑制するために何ができるか、また、ページの DOM をできる限り小さくした後のレンダリング作業を最適化するその他の戦略を理解することが重要です。
大きな DOM がページのパフォーマンスに与える影響
大きな DOM は、ページ パフォーマンスに次のような影響を及ぼします。
- ページの初回レンダリング時。CSS をページに適用すると、CSS オブジェクト モデル(CSSOM)と呼ばれる DOM に似た構造が作成されます。CSS セレクタの限定性が高くなると、CSSOM は複雑になり、ウェブページを画面に描画するために必要なレイアウト、スタイル設定、合成、ペイントの作業を実行するのに時間がかかります。この追加の作業により、ページ読み込みの早い段階で発生するインタラクションのレイテンシが増加します。
- 要素の挿入や削除、または DOM のコンテンツやスタイルの変更によってインタラクションによって DOM が変更される場合、その更新をレンダリングするために必要な作業で、レイアウト、スタイル設定、合成、ペイント作業に多大なコストがかかる可能性があります。ページの最初のレンダリングと同様に、操作の結果として HTML 要素が DOM に挿入されたときは、CSS セレクタの特異性が高まることで、レンダリング作業が増加する可能性があります。
- JavaScript が DOM に対してクエリを実行する際、DOM 要素への参照はメモリに格納されます。たとえば、
document.querySelectorAll
を呼び出してページ上のすべての<div>
要素を選択する場合、結果で多数の DOM 要素が返されると、メモリの消費が大きくなる可能性があります。
これらすべてがインタラクティビティに影響を与える可能性がありますが、上記のリストの 2 つ目は特に重要です。操作によって DOM が変更されると、ページの INP が低下する原因となる多くの作業が始動する可能性があります。
DOM サイズの測定方法
DOM のサイズはいくつかの方法で測定できます。1 つ目の方法では Lighthouse を使用します。監査を実行すると、現在のページの DOM に関する統計情報が [過剰な DOM サイズの回避] に表示されます。[診断]タブで見出します。このセクションでは、DOM 要素の合計数、最も子要素が多い DOM 要素、および最も深い DOM 要素を確認できます。
より簡単な方法は、主要なブラウザでデベロッパー ツールの JavaScript コンソールを使用することです。DOM 内の HTML 要素の総数を取得するには、ページが読み込まれた後、コンソールで次のコードを使用します。
document.querySelectorAll('*').length;
DOM サイズの更新をリアルタイムで確認したい場合は、パフォーマンス モニター ツールも使用できます。このツールを使用すると、レイアウトやスタイル設定の操作(およびその他のパフォーマンス要素)と、現在の DOM サイズを関連付けることができます。
<ph type="x-smartling-placeholder">DOM のサイズが Lighthouse の DOM サイズの警告しきい値に近づいている場合、または完全に失敗した場合、次のステップとして、DOM のサイズを縮小して、ユーザーの操作に対するページの機能を改善し、ウェブサイトの INP を改善する方法を検討します。
インタラクションの影響を受けた DOM 要素の数を測定するにはどうすればよいですか?
ページの DOM のサイズと関係があると思われる、遅いインタラクションをラボでプロファイリングしている場合は、プロファイラで [Recalculate Style] というラベルのアクティビティを選択すると、影響を受けた DOM 要素の数を確認できます。下部のパネルでコンテキスト データを確認できます。
<ph type="x-smartling-placeholder">上のスクリーンショットで、作業のスタイル再計算を選択すると、影響を受ける要素の数が表示されます。上のスクリーンショットは、DOM 要素が多いページでのレンダリング作業に対する DOM サイズの影響の極端なケースを示していますが、この診断情報は、操作に対する次のフレームの描画にかかる時間の制限要因が DOM のサイズになっているかどうかを判断するのに役立ちます。
DOM のサイズを小さくするにはどうすればよいか
ウェブサイトの HTML を監査して不要なマークアップがないかどうかを調べるだけでなく、DOM のサイズを小さくする主な方法は DOM の深さを減らすことです。DOM が不必要に深くなっている可能性があることを示すシグナルとして、ブラウザのデベロッパー ツールの [要素] タブに次のようなマークアップが表示されていることがあります。
<div>
<div>
<div>
<div>
<!-- Contents -->
</div>
</div>
</div>
</div>
このようなパターンを見かけた場合、おそらく DOM 構造をフラット化することでパターンを簡素化できるでしょう。そうすることで、DOM 要素の数が減り、ページのスタイルをシンプルにできます。
DOM 深度は、使用するフレームワークの兆候の場合もあります。特に、コンポーネント ベースのフレームワーク(JSX に依存するフレームワークなど)では、親コンテナに複数のコンポーネントをネストする必要があります。
ただし、多くのフレームワークでは、フラグメントと呼ばれるものを使用することで、コンポーネントのネストを回避できます。フラグメントを機能として提供するコンポーネント ベースのフレームワークとしては、次のようなものがあります(これらに限定されません)。
任意のフレームワークでフラグメントを使用することで、DOM の深さを軽減できます。DOM 構造のフラット化がスタイル設定に与える影響に懸念がある場合は、Flexbox や grid など、より新しい(そして高速な)レイアウト モードを使用すると効果的な場合があります。
考慮すべきその他の戦略
苦労して DOM ツリーをフラット化し、不要な HTML 要素を削除して DOM をできる限り小さくする場合でも、DOM はかなり大きくなる可能性があり、ユーザーの操作に応じて変化するため、レンダリング作業が多くなってしまうことがあります。このような状況になった場合は、レンダリング作業を制限するために他の方法を検討できます。
追加的なアプローチを検討する
初回レンダリング時には、ページの大部分がユーザーに表示されない場合があります。これは、起動時に DOM の該当部分を省略して HTML を遅延読み込みできる機会となり、ユーザーがページの初めは隠していた部分を必要とする部分をユーザーが操作したときに追加できる可能性があります。
このアプローチは、初期読み込み時だけでなく、その後にも有用です。最初のページ読み込みでは、前もって行うレンダリング作業が少なくなります。つまり、最初の HTML ペイロードが軽量になり、レンダリングがより速くなります。これにより、その重要な期間に、メインスレッドのアテンションに対する競合を抑えながら、インタラクションを実行する機会を増やすことができます。
読み込み時に最初に非表示になっているページ部分が多い場合、再レンダリング処理をトリガーする他の操作も速くなります。ただし、他のインタラクションによって DOM がさらに増えていくと、ページのライフサイクルを通じて DOM が拡大するにつれてレンダリング作業が増加します。
徐々に DOM に追加するのは難しく、それにもトレードオフがあります。この方法では、ユーザーの操作に応じてページに追加する HTML に入力するデータを取得するために、ネットワーク リクエストを行うことになります。処理中のネットワーク リクエストは INP にカウントされませんが、認識されるレイテンシが増加する可能性があります。可能であれば、データを取得中であることを示す読み込みスピナーなどのインジケーターを表示して、ユーザーが何かが発生していることを認識できるようにします。
CSS セレクタの複雑さを制限する
ブラウザは CSS のセレクタを解析する際、DOM ツリーを走査して、そのセレクタが現在のレイアウトにどのように適用されるか、また適用されているかを把握する必要があります。これらのセレクタが複雑になるほど、ブラウザはページの最初のレンダリングを実行するだけでなく、操作の結果としてページが変更された場合にスタイルの再計算とレイアウト作業も増加します。
content-visibility
プロパティを使用する
CSS には content-visibility
プロパティが用意されています。このプロパティを使用すると、画面外の DOM 要素をゆっくりとレンダリングできます。要素がビューポートに近づくと、オンデマンドでレンダリングされます。content-visibility
のメリットは、最初のページ レンダリング時にレンダリング作業を大幅に削減できるだけでなく、ユーザー インタラクションの結果としてページの DOM が変更されたときに、画面外要素のレンダリング作業もスキップします。
まとめ
ウェブサイトの INP を最適化するには、DOM のサイズを本当に必要なサイズに減らすことをおすすめします。そうすることで、DOM が更新されたときにブラウザがレイアウトとレンダリングを実行するのに要する時間を短縮できます。DOM サイズを大幅に削減できない場合でも、CSS 包含や content-visibility
CSS プロパティなど、レンダリング作業を DOM サブツリーに分離するための手法があります。
どのような方法であれ、レンダリング作業を最小限に抑えられる環境を作り、操作に対するページのレンダリング作業を減らすことで、ユーザーが操作した際のウェブサイトの応答性が高くなります。つまり、ウェブサイトの INP が低くなり、ユーザー エクスペリエンスが向上します。
Louis Reed 作の Unsplash のヒーロー画像