読者です 読者をやめる 読者になる 読者になる

フロントエンド開発入門編(レンダーツリーの構築、レイアウト、ペイント)

前回

uraway.hatenablog.com

自分の理解のために訳しています。公式の日本語訳の方がわかりやすいです。

Render-tree construction, layout, and paint | Web Fundamentals - Google Developers

Render-tree construction, layout, and paint | Web Fundamentals - Google Developers (日本語訳)

レンダーツリーの構築、レイアウト、ペイント

はじめに

CSSOMツリーと DOMツリーを組み合わせたレンダーツリーを使って、表示される各エレメントのレイアウトを計算します。また、画面にピクセルをレンダリングするペイント処理の入力としても使用されます。手順それぞれを最適化することは、レンダリングのパフォーマンスを最適化することにおいて重要になります。

オブジェクトモデルの構築について説明した前のセクションでは、HTML入力とCSS入力をベースにDOMツリーとCSSOMツリーを構築しました。しかし、この2つは異なる角度からドキュメントを表す独立したオブジェクトです。一方はコンテンツを記述し、もう一方はドキュメントに適用する必要があるスタイル規則を記述します。この2つを組み合わせ、ブラウザの画面にピクセルをレンダリングするにはどうすればよいでしょうか。

TL;DR

DOMツリーとCSSOMのツリーはレンダーツリーを形成する。

レンダーツリーはウェブページをレンダリングするために必要なノードのみを含む。

レイアウトは各オブジェクトの正確な位置とサイズを計算する。

ペイントは構築されたレンダーツリーで行われる最後の作業であり、画面にピクセルを描画する。

レンダーツリーの構築、レイアウト、ペイント

最初のステップはブラウザでDOMツリーとCSSOMツリーをレンダーツリーとして組み合わせます。レンダーツリーはすべての表示されるDOMコンテンツと各ノードに対するすべてのCSSOMスタイルの情報を取得します。

レンダーツリーを構築するために、ブラウザは基本的に次のことを行います。

  1. DOMツリーのルートから表示される各ノードを横断します。
  2. まったく表示されないノード(<script>タグ、<meta>タグなど)は、レンダリング出力には反映されないので省略されます。

  3. CSSによって表示されず、レンダーツリーから除外されるノードもあります。上記の例での<span>ノードは"display: none"プロパティを持つのでレンダーツリーからは除外されます。

  4. 表示される各ノードに対して、適切な一致するCSSOMルールを適用します。

  5. コンテンツと適応されたスタイルを持つノードを表示します。

Remember

'visibility: hidden'は'display: none'と異なります。前者はエレメントを非表示にしますが、レイアウトスペースは使用しています。(つまりからのボックスとしてレンダリングされます。)対して、後者はエレメントをレンダーツリーから取り除きます。従ってエレメントは不可視になり、レイアウトスペースもありません。

最終的なアウトプットは画面上に表示されるすべてのコンテンツとスタイル情報を含むレンダリングです。もうすぐです!レンダーツリーが正しく表示されたら、「レイアウト」段階に進むことができます。

ここまで、どのノードを表示するのか、スタイルをどうするのかを計算しましたが、デバイスのビューポートにおける位置とサイズを決定していません。これを決定するのがレイアウトの段階で、リフローとしても知られるものです。

各オブジェクトの正確なサイズと位置を知る為、ブラウザはレンダーツリーのルートから各ノードを調べ、ページ上の各オブジェクトの形状を計算します。簡単な実践的なサンプルを見てみましょう。

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Critial Path: Hello world!</title>
  </head>
  <body>
    <div style="width: 50%">
      <div style="width: 50%">Hello world!</div>
    </div>
  </body>
</html>

上記のページには、ネストされた2つのdivがあります。1つ目(親)はノードの表示サイズをビューポイントの幅の50%に設定しています。また、2つ目のdivは幅を親の50%に設定しています。つまりビューポイントの幅の25%です。

レイアウト処理の出力はビューポイント内の各エレメントの正確な位置とサイズを取得した、”ボックスモデル”と呼ばれるものです。すべての関連する測定値は画面上の絶対的なピクセル位置に変換される、などです。

どのノードが表示されるのか、それらのノードの適応されるスタイル、形状がわかったので、最後にこれらの情報をレンダーツリーの各ノードを画面上の実際のピクセルに変換する最終段階へと渡します。この段階は、ペインティングやラスタラインジングと呼ばれます。

全部理解できましたか?これらの各ステップはブラウザによる大量の仕事を必要としており、かなり時間がかかることもあります。ありがたいことにChrome DevToolsは上記で説明した3つの段階すべての情報を提供しています。

  • レンダーツリーの構築と位置とサイズの計算はTime Lineの"Layout"イベントで見ることができます。
  • レイアウトが完了すると、ブラウザはレンダーツリーを実際の画面上のピクセルに変換する"Paint Setup"と"Paint"イベントを発行します。

レンダーツリーの構築、レイアウト、ペイントの処理に必要な時間は、ドキュメント、適応したスタイル、そしてもちろん実行中のデバイスのサイズによって変わります。ドキュメントが大きければ大きいほど、ブラウザが行う仕事は多くなります。スタイルが複雑であればあるほど、ペインティングにかかる時間は長くなります。(例えば、単色はペイントのコストパフォーマンスが良く、ドロップシャドウはコストパフォーマンスが悪いということです。)

すべてが言及され、完了すると、ページがビューポートに表示されます。

ブラウザが行ったステップすべてを簡単に要約しましょう。

  1. HTMLマークアップを処理してDOMツリーを構築する。
  2. CSSマークアップを処理してCSSOMツリーを構築する。
  3. DOMツリーとCSSOMツリーを組み合わせ、レンダーツリーを構築する。
  4. レンダーツリー上でレイアウトを実行し、各ノードの形状を計算する。
  5. 画面に各ノードをペイントする。

デモページはとてもシンプルに見えますが、多くの処理が必要です。DOMあるいはCSSOMが修正された時になにが起こるでしょうか?同じ処理を再度繰り返して、画面に再びレンダリングするべきピクセルを判断しなければなりません。

レンダリングパスを最適化することは、上記の1から5の処理に費やす時間の合計を最小化することになります。 これを行うことで、可能な限り早く画面にコンテンツをレンダリングすることができ、また、最初にレンダリングした後、画面のアップデート間にかかる時間を減らすこともできます。つまりインタラクティブなコンテンツを高頻度に更新することができます。

Authors
Ilya Grigorik
Ilya is a Developer Advocate and Web Perf Guru