はじめに
ブラウザはアドレスバーに入力されたURLをもとにHTTPリクエストを作成し、Webサーバーへ送ります。
Webサーバーから返されるHTTPレスポンスにはステータスラインやレスポンスヘッダ、レスポンスボディが含まれています。
レスポンスボディにはHTMLや画像などが含まれますが、ブラウザはこのHTTPレスポンスからHTMLを受け取り、解析・表示を行っています。
本記事では、HTMLがどのように解析され、DOMツリーが構築されていくのかの概要についてみていきます。
アドレスバーにURLを入力してからHTMLを受け取るまで
アドレスバーにURLを入力してからHTMLを受け取るまでの流れを簡単にまとめてみます。
URLの解析
まず、アドレスバーに入力されたURLを解析し、スキーム(HTTP/HTTPSやFTP、FILEなど)、ホスト(ドメイン名)、ポート番号、パス、クエリパラメータなどの構成要素に分割します。
DNSによる名前解決
ドメイン名からリクエスト先のIPアドレスを取得します(名前解決)。
まずはhosts
ファイルやDNSキャッシュ1を確認し、対象のレコードがないかを確認します1。
キャッシュがなければブラウザはOSのDNSリゾルバに問い合わせを行います。
OSのDNSリゾルバは(ISPなどの)ローカルDNSサーバやルートDNSサーバ、トップレベルドメインサーバや権威サーバへと順次問い合わせていき、IPアドレスを取得します。
TCP接続の確立
IPアドレスが判明したら、ブラウザはそのサーバとTCP接続を確立します(3ウェイハンドシェイク)。
TLS(HTTPSの場合)
HTTPSを使用している場合には、ブラウザとサーバーの間でTLSハンドシェイクが行われ、通信が暗号化されます。
HTTPリクエストの送信
ブラウザはHTTPリクエストを作成してサーバーに送信します。
HTTPリクエストにはリソースのパス、HTTPメソッド(GETなど)、ヘッダー情報などが含まれます。
サーバーからHTTPレスポンスが返される
サーバーはHTTPレスポンスをブラウザに送ります。
HTTPレスポンスにはステータスラインやレスポンスヘッダ、レスポンスボディ(HTML、CSS、JavaScriptなどのコンテンツを含む)が含まれます。
大まかには上記の流れで、アドレスバーにURLを入力してからHTMLを受け取ります。
レンダリングエンジン
レンダリングエンジンとは、ブラウザ内にあるHTMLの描画のためのコンポーネントです。
HTMLはレンダリングエンジンのHTMLパーサーによって解析されます。
この解析結果をもとに、レンダリングエンジンはDOMツリーを構築します。
レンダリングエンジンにはいくつか種類があり、ブラウザによって採用されているものが異なります。
Google Chrome やMicrosoft Edge2ではBlinkというレンダリングエンジンが採用されています。
また、Safari ではWebKit、FireFox ではGecko が採用されています。
ちなみに、ブラウザのシェア率は以下のサイトで確認できます。
HTMLの解析
レンダリングエンジンはまずHTMLパーサーによって、HTML文字列に対して字句解析・構文解析を行い、その結果をもとにDOMツリーを構築します。
字句解析
レンダリングエンジンのHTMLパーサーは、HTMLを構成する文字列をトークンと呼ばれる、「意味を持つ最小の単位」へと分割します。
<!DOCTYPE html>
などが意味を持つ最小の単位となります。
他にも、開始タグを表すトークンや終了タグを表すトークン、文字トークンやコメントトークン、空要素タグトークンなどへとそれぞれ分割されていきます。
構文解析
字句解析によってトークン化された結果をもとに、構文木と呼ばれる木構造を構築していきます。
トークン化された要素の親子関係、入れ子構造などを階層構造に反映していきます。
DOMツリーの構築
構文木をもとにして、DOMツリーを作成していきます。
構文木をもとにパーサーがDOMツリーに1つずつ要素を追加していきます。
ここで、<script>
タグが見つかった場合には、パーサーの処理を一時的に停止してJavaScriptを実行します。
パーサーの処理を一時的に停止するのは、JavaScriptによってDOMツリーの構造に変更がはいる可能性があるためです。
パーサーの処理が停止することはパフォーマンス上もよくないため、実行タイミングをDOMツリー構築後に遅らせるdefer
属性を使用することでパーサーの一時停止を回避する方法をとることもできます。
また、外部スクリプトの場合にはダウンロード中もパーサーが停止してしまいます。
そのような場合には、async
属性を<script>
タグに付与することで、ダウンロード中はパーサーを停止させず、ダウンロードが完了し次第パーサーを一時停止してJavaScriptを実行させることもできます。
また、JavaScriptからはこの時点までで構築されているDOMツリーにアクセスすることも可能です。
getElementById
などで要素を取得する際に、<script>
タグよりも前に記述されている要素しか取得できないのは、<script>
タグの後ろに記述されている要素がまだDOMツリーへ追加されていないためです。
以上の流れでDOMツリーが構築されていきます。