フロントエンドエンジニアをやっています。
頑張るから読んでほしい。

ブラウザのレンダリングの仕組み

「ブラウザを立ち上げてアドレスバーにURLを打ち込んでEnter押してからページが表示されるまでに (裏側で) 何が起こっているかわかる限り説明してみてください。」

っていう問題を先輩から出題されたのですが、上手く答えられず...

ブラウザのレンダリングについてWebフロントエンド ハイパフォーマンス チューニングを読んで勉強したのでそのmemoです。

1. レンダリングエンジン

ブラウザ レンダリングエンジン JavaScriptエンジン
Google Chrome Blink V8
IE Trident Chakra
Microsoft Edge EdgeHTML Chakra
Firefox Gecko SpiderMonkey
Safari Webkit Nitro(JavaScriptCore)

Blink,WebKit,Geckoオープンソースレンダリングエンジン。 BlinkはWebKitをフォークして誕生。

ブラウザ内のコンポーネント

いくつかのソフトウェアコンポーネントによって構成されている。

知っておきたい2つの重要なコンポーネントは以下。

レンダリングエンジンとは?

JavaScriptエンジンとは?

CSSOMとは?

CSSOMは、CSS Object Modelを意味する略語であり、ブラウザでロードされたCSSのツリー構造を保持する仕組み。ブラウザは、このCSSのツリー構造をDOMに対して適用することで、上位のスタイルからより具体的な下位のスタイルへと連鎖的にスタイルを決定していく。

2. ブラウザのレンダリングの流れ

レンダリングの大まかな流れ

Loading(リソース読み込み)

Scripting(JavaScript実行)

Rendering(レイアウトツリー構築)

Painting(レンダリング結果の描画)

4つの工程からレンダリングが始まって、最終的に描画されるまでをフレーム(Frame)と呼ぶ。

3. リソース読み込み

まず行われるのがリソース読み込み

ブラウザは、与えられたURLからHTMLを読み込んで、そこからさらにレンダリングに必要な付属するリソースを読み込んで解釈する このフェーズでは、次の2つの処理がある。

  • リソースのダウンロード
    • HTMLを含むリソースをサーバーからダウンロードする。
  • リソースのパース
    • ダウンロードしたリソースをパース(構文解析)してレンダリングエンジンの内部表現に変換する
      • HTMLやCSSは、それぞれDOMツリーやCSSOMツリーの変換される

リソース

  • HTMLファイル
  • CSSファイル
  • JS画像ファイル

3.1 リソース取得に用いるネットワークプロトコル

ブラウザは与えられたURLをもとにレンダリングをもとにレンダリングに必要なリソースを様々なネットワークプロトコルを通じて取得する。

ブラウザでよく利用されるネットワークプロトコルHTTP

  • URLに含まれるホスト名の解決
  • HTTPによる取得
    • TCP接続の確立
    • HTTPSであれば)TLS接続の確率
    • HTTPリクエストの送信とHTTPレスポンスの受け取り

IP(Internet Protocol)

ネットワークのノード間のパケットのやり取りを中継するプロトコル

HTTPでネットワーク越しにリソースを取得する場合、リソースの取得の速さはIPのパケットの届く速度に依存する。 →HTTPでやり取りされるデータパケット内に格納されるから。

パケット データの通信に利用される最小単位のデータ。 通信するデータの入れ物としてやり取りされる

TCP(Transmission Control Protocol)

TCPは、IPに対して以下のような機能を付加する上位プロトコル

  • 相手先に確実にデータが届いているかどうか確認
  • データの欠損や破損をけんちして再送
  • データの送信順を保証する

TLS(Transport Layer Security)

与えられたURLのプロトコル部分がhttpsだった場合TCPとHTTPの間でTLSプロトコルを利用する。 TLSは一般にSSLと呼ばれる。

  • クライアントとサーバーの認証機能
  • 通信データの暗号化
  • データの改ざんの検出

UDP (User Datagram Protocol)

IPに対して機能を付加。 TCPと似ている。 TCPプロトコルは、IPに対してデータの欠損の検知やデータの送信順の保証などにお信頼性のあるデータ通信を提供するがUDPではそのような機能は特に追加しない。

HTTP

  • ウェブページのコンテンツを送受信するために利用されるプロトコル
  • シンプル
  • クライアントはサーバーに対してHTTPリクエストを送信し、サーバーはクライアントに対して- - HTTPレスポンスを返す。

DNS(Domain Name System)

IPアドレスを解決するためのシステム、プロトコル

TCP接続では、接続を開始するのに相手方のIPアドレスが必要。 ブラウザはURLに含まれているホスト名をIPアドレスに変換した上でHTTPリクエストをサーバーへと送信する。

DNSはホスト名に紐づくIPアドレスを検索するための分散システム。

4. それぞれのリソースの読み込み

  • ブラウザはホスト名の解決などを経て、HTML,CSS,JacaScriptや画像といったリソースを取得する。

  • 一番最初に読み込まれるリソースはHTMLファイル。

  • HTMLファイル内に記述されているリソースの参照があれば、さらにそのリソースを読み込む。
  • 取得したリソースは、パースされてブラウザの内部表現に変換される。
    • HTMLコンテンツの場合はDOMツリーに
    • CSSの場合はCSSOMツリーに
    • それ以外にリソースも、レンダリングエンジンの実装に沿った内部表現に変換される。

HTMLの読み込み

  1. ブラウザは与えられたウェブページのURLを元にサーバーへHTTPリクエストを送信。 HTTPレスポンスとしてHTMLを取得する。
  2. 読み込んだHTMLを解釈してドキュメントのDOMツリーを構築する。DOMツリーへの変換過程の中でツリーに含まれる画像やCSSなどのドキュメントに紐づくリソースに取得や読み込みを行う。
  3. ブラウザは構築したDOMツリーを元にしてRenderingの処理を行う。

DOM(Document Object Model

HTMLのドキュメントを表現するオブジェクト。 DOMツリーは、レンダリングエンジンが利用する木構造を持つ内部表現。



f:id:cidermitaina:20190303224436p:plain

DOMツリーへの変換の工程

  1. 字句解析によるトークンのリスト化
  2. 構文解析による構文木構築
  3. 構文木内にあるJavaScriptを実行しつつDOMツリーの構築

字句解析

コンパイラーがソースコードを解析し、目的のプログラムを生成する際の処理工程のひとつ。字句解析は、ソースコードに記述された変数や定数などの値を実際の値に展開するまでを担当し、その結果を次の工程である構文解析に引きわたす。

トーク

1つの塊になっている文字列

CSSの読み込み

読み込まれたCSSは、レンダリングエンジンによってパースされてCSSOM(CSS Object Model)ツリーへと変換される。

f:id:cidermitaina:20190303224508p:plain



DOMツリーと違ってCSSOMツリーの深さは可変ではなく、一定ですが、ルールセットが増えれば増えるほどDOMツリー内の要素に適用されるスタイル計算にかかる時間が大きくなる。

5. JavaScript実行 Scripting

リソースを一式読み込んだ後、JacaScript実行(Scripting)へ移行。

レンダリングエンジンは、JavaScriptのコードをJavaScriptエンジンに引き渡して実行させる。

JavaScript実行の流れ

JavaScriptコード)

    ↓

  字句解析

    ↓

 (トークン列)

    ↓

  構文解析

    ↓

 (抽象構文木

    ↓

  コンパイル

    ↓

 (実行可能コード)

    ↓

   実行

JavaScriptエンジンの実装によって異なる。

JavaScriptの実行は、最初にJavaScriptファイルを読み込んだとき以外にも、DOMイベントが発火し、イベントリスナが起動するときに起こる。

5.1 字句解析と構文解析

JavaScriptエンジンは、与えられたコードを何らかの実行可能な形式に変換(コンパイル)した上でJavaScriptに書かれた処理を実行する。

コンパイルを行うために、JavaScriptのコードを抽象構文木(英: abstract syntax tree、AST)と呼ばれるコンパイル可能な形に変換する。 JavaScriptの場合はJavaScriptオブジェクト(JSON)として表現される。

抽象構文木

構文構造をデータ構造に起こしたもの。JavaScriptの文法に沿った形で表現される木構造のデータ。

抽象構文木

{
  "range": [
    0,
    10
  ],
  "type": "Program",
  "body": [
    {
      "range": [
        0,
        10
      ],
      "type": "VariableDeclaration",
      "declarations": [
        {
          "range": [
            4,
            9
          ],
          "type": "VariableDeclarator",
          "id": {
            "range": [
              4,
              5
            ],
            "type": "Identifier",
            "name": "a"
          },
          "init": {
            "range": [
              8,
              9
            ],
            "type": "Literal",
            "value": 1,
            "raw": "1"
          }
        }
      ],
      "kind": "var"
    }
  ],
  "sourceType": "module"
}

5.2 コンパイル

JavaScriptエンジン内部のコンパイラは、先ほど構築した抽象構文木を実行可能な形式にコンパイルする。

スクリプト言語の言語処理系の実装の方法

JavaScriptエンジンで多いのは、JIT(Just In Time)コンパイル型の実装

JIT(Just In Time)コンパイル

5.3 実行

実行可能な形式にコンパイルされたJavaScriptのコードは、処理系内部の仮想マシン、もしくはCPUで実行される。

6. レイアウトツリー構築 - Rendering

JavaScriptの実行が終わると、レイアウトツリー構築(Rendering)が行われる。

具体的に以下

  • スタイルの計算
  • レイアウト

レイアウトツリー

ブラウザで DOM と CSSOM を組み合わせて、ページ上の表示可能なすべての DOM コンテンツと、各ノードのすべての CSSOM スタイル情報を取り込んだもの

6.1 スタイルの計算

DOMツリー内の全てのDOM要素に対して、どのようなCSSプロパティが当たるのか計算する。

  1. CSSOMツリー内を全て参照して、CSSルールのCSSセレクタマッチング処理が行われる。
  2. CSSルールの詳細度を算出して個別のDOM要素に対して、どのようなCSSプロパティが適用されるか判断

CSSルールのマッチング処理

CSSOMツリーからCSSルールセットを走査、DOMツリーからDOM要素を走査。 どんなCSSルールが適用されるのかを計算する。

CSS セレクタのマッチング

CSSセレクタのマッチング処理は右側から行われる。

body > .container > .button {
    ...
}
  1. DOM要素のclass属性にbutton
  2. 親要素のclass属性にcontainer
  3. 親要素のDOM要素名がbody

レンダリングエンジンはセレクタを右から左に解釈してマッチング処理を行う

どのDOM要素に対してどのCSSルールセットが適合するかレンダリングエンジンには分かるようになる。

CSSルールセット

div.my-button, button {
    background-color:  green;
    color: white;
}

{}部分

適用されるCSSプロパティの算出

DOM要素にどのCSSプロパティと値が適用されるのか算出する。

(margin, padding,positionの算出ではない)

詳細度の計算など

<p class="foo">
    foo
</p>

.foo {
 color: red;
}

p {
  color: blue;
}

.fooのスタイルが当たることの計算

6.2 レイアウト

DOM要素に当たるCSSプロパティを算出した後、レンダリングエンジンはDOMツリー内のすべてのノードの視覚的なレイアウト情報の計算、レイアウトを行う。

設計図的なもの

レイアウト情報

  • 要素の大きさ
  • 要素のmargin
  • 要素のpadding
  • 要素の位置

7. レンダリング結果の描画 -Painting

DOMツリーのレイアウト情報の算出が終わると、レンダリング結果の描画(Painting)

レンダリングエンジンはユーザーが見ることができる実際のピクセルを描画。

3つの処理が行われている。

  • ペイント(Paint)
  • ラスタライズ(Rasterize)
  • レイヤーの合成(Composite Layers)

最後のレイヤーの合成が終わることで、ユーザーの目にはレンダリングエンジンが描画した表示になる。

7.1 ペイント

  • 内部の低レベルな2Dグラフィックエンジン向けの命令を生成。

    • RenderTreeを元にDisplay Listと呼ばれる内部の低レベルグラフィックエンジンのための命令の列を生成。
  • 組み込まれるグラフィックエンジンはブラウザの実装ごとに異なる

    • WebKit 異なるグラフィックエンジンを組み込めるように設計されている。
    • Blink  Skiaが使われている 。
    • Safari CoreGraphicsが使われている

7.2 ラスタライズ

  • 生成された命令を用いて実際にピクセルに描画する。
  • レイヤーごとに一枚一枚描画される。
  • レイヤーが生成されるのは、 positiontransform ,opacityなどのプロパティが適用されているとき

レイヤー単位でピクセルに描画するのはなぜ? 再レンダリングする場合、すでに描画が終わったレイヤーを再利用することで、素早く再レンダリングできる場合がある。

7.3 レイヤーの合成

ピクセルにしたレイヤーを合成して最終的なレンダリング結果を生成する。

  • CSS基本的にはCPUによって合成される
  • transformCSSプロパティに3D変形関数を指定するとGPUによって合成される

CPU

コンピューター全体の計算処理

GPU

3Dグラフィックなどの画像描写に必要な計算処理

7.4 コンテンツの表示

レイヤーの合成の処理を終えてやっと

レンダリングエンジンがコンテンツを表示します!

8 再レンダリング

ユーザーやブラウザの何らかのアクションやJavaScriptのコードの実行やドキュメント内のイベントによってレンダリングは再度引き起こされる。

  • 全てのレンダリングの処理が最初からやり直しになるわけではない。
  • 多くのレンダリングエンジンは、これまで描画のために構築した内部表現のオブジェクトをなるべく再利用しようとする

レンダリングが引き起こされるのは?

  • DOMイベントが発火するとき





「ブラウザを立ち上げてアドレスバーにURLを打ち込んでEnter押してからページが表示されるまでに (裏側で) 何が起こっているかわかる限り説明してみてください。」

はじめは、この質問を何も見ずに15分考えると、ぼんやりとした流れを説明することしかできなかったのですが、ブラウザの仕組みを勉強したあとは3倍くらいの量できちんと説明できるようになりました◎





参考

Webフロントエンド ハイパフォーマンス チューニング

Webフロントエンド ハイパフォーマンス チューニング

  • 作者:久保田 光則
  • 発売日: 2017/05/26
  • メディア: 単行本(ソフトカバー)