Webを見ているときに起きていること

この記事は[SmartHR Advent Calendar 2019 5日目](https://qiita.com/advent-calendar/2019/smarthr)の記事です。

## はじめに

こんにちは、フロントエンドエンジニアの[@cidermitaina](https://twitter.com/cidermitaina)です。

みなさん、Webを見ているときにブラウザで起きていることって考えたことありますか？
ちなみに私は考えたことがなかったです。。。

アドレスバーにURLを入力すると、すぐに見たいページが表示される、このことが当たり前だと思っていたのですが、実はブラウザの中では様々のことが起きているのです。

今回はそんなブラウザについての記事です。

アドレスバーにURLを入力してから起きていることについて追っていこうと思います。

## アドレスバーにURLを指定されたWebブラウザの動作

### 1. `http://www.example.com`をアドレスバーに入力。

まずはブラウザのアドレスバーにURLを入力します。

![68747470733a2f2f6573612d736d61727468722e73332d61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f75706c6f6164732f70726f64756374696f6e2f6174746163686d656e74732f31313434392f323031392f31312f33302f36333932342f66356636363631342d363337362d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/d69705c5-4406-4442-4dd6-6fce894401c1.png)



### 2. `www.example.com`のIPアドレスを探す（DNS解決）
`www.example.com`サーバーにあるリソース（HTML)を取得したいというリクエストを送りたいのですが、`www.example.com`のままではブラウザは`www.example.com`のサーバーの場所は分かりません。

`www.example.com`のサーバーの場所を探すためにルートサーバーに`www.example.com`のIPアドレスを問い合わせます。

![68747470733a2f2f6573612d736d61727468722e73332d61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f75706c6f6164732f70726f64756374696f6e2f6174746163686d656e74732f31313434392f323031392f31312f33302f36333932342f62646630653338342d386432612d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/c2226b2e-ba0a-201f-2a6c-d6f7ede90547.png)





### 3. `www.example.com`のIPアドレスに対してTCPの80番ポートで接続する

`www.example.com`のIPアドレスが分かったので、さっそくサーバーにリクエストを送りたいのですが、Webを含む、インターネットにおける通信の大半がTCPを前提にしています。

TCPで通信を行うためにはまず接続するという作業が必要です。

1. 通信を開始したい側が「このポート番号で接続させてください」という接続要求パケットを送信する
1. その接続を受け入れるのであれば「いいですよ」という応答を返す
1. 受け取った通信を開始したい側は「ありがとうございます。お願いします！」という内容を送信してTCP接続が確立。

(↑は3回メッセージがやり取りされるので、**3 way handshake** と呼ばれています。)

![68747470733a2f2f6573612d736d61727468722e73332d61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f75706c6f6164732f70726f64756374696f6e2f6174746163686d656e74732f31313434392f323031392f31312f33302f36333932342f31373434303332632d316630372d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/5c6a705d-e065-c540-1577-ee06f1064d53.png)


### 4. リクエストメッセージを送信する

TCP接続が成功したので、やっと`www.example.com`にリクエストを送れます。

WebブラウザとWebサーバーの間ではHTTPでやり取りをしているので、HTTPリクエストメッセージを送信します。

![image (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/adba9887-1bff-cdf9-d4d5-4b3982a0d7ec.png)


（HTTPリクエストメッセージはChrome DevToolsのNetworkパネルから確認することができます。）
![image (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/2ff7b301-e190-e5b6-163d-2af6f691a22c.png)



### 5. サーバーがメッセージを受け取り、レスポンスメッセージを返信する。

WebブラウザからのHTTPリクエストを受け取ったWebサーバーは、要求されたリソースをWebブラウザに返します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/0cbe2c56-e256-ab6d-f1a2-6bcf0f40b533.png)



### 6. リソースの読み込み（loading）

リソースを受け取ったWebブラウザですが、受けとったリソースのHTMLをそのまま表示させる訳にはいきません。

解釈した上で画面に表示しなければなりません。

#### リソースのDownload

HTMLをサーバーから受け取りましたが、どうやらHTMLには画像やCSS参照されている記述があります。

ブラウザは受け取ったHTMLにCSS,JavaScriptや画像といったリソースの参照があれば、さらにそのリソースを読み込みます。

#### リソースの解釈 (Parse)

```index.html
<html>
<head></head>
<body>
  <h1>My Header</h1>
  <a href="www.example.com">My link</a>
</body>
</html>
```
この状態ブラウザに表示させても全く分からないので、ブラウザがRenderingしやすいように読み込んだHTMLを解釈してドキュメントの**DOMツリー**を構築していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/56b339f9-5f7d-220f-c31e-34c16e4faf1b.png)



同じようにCSSも解釈され、CSSOMツリーを構築していきます。
![image (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/3d07d35c-46ff-06fb-78f6-81227e25d879.png)


### 8. JavaScriptの実行 (Scripting)

リソースを一式読み込んだ後はJavaScript実行（Scripting）します。

レンダリングエンジンは、JavaScriptのコードを実行することができないのでJavaScriptエンジンに引き渡して実行させます。

ですが、このままのコードでは、JavaScriptエンジンは、実行することができません。
実行可能な形式に変換（コンパイル）しないと処理を実行することができないのです。

コンパイルを行うために、JavaScriptのコードを抽象構文木（AST:abstract syntax tree）と呼ばれるコンパイル可能な形に変換します。

1. 字句解析を行い、トークン列に。
![image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/b2e695b0-0962-aeec-4f00-d5962266e334.png)

2. トークン列をもとに構文解析を行い抽象構文木（AST）に。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/82a72e51-1501-b9a8-5588-b6464bc410c3.png)


3. 抽象構文木を実行可能な形式にコンパイル
4. 実行

### 9. レイアウトツリー構築 (Rendering)
JavaScriptの実行が終わるといよいよ画面に表示する準備に取り掛かります。

DOM と CSSOM を組み合わせて、ページ上の表示可能なすべての DOM コンテンツと、各ノードのすべての CSSOM スタイル情報を取り込んだ**レイアウトツリー**を作成していきます。

![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/963b19f7-90c2-6c61-ba2c-fe4240f0f6dc.png)
![image (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/b420689a-5854-e522-ddfc-ca4048adc012.png)


#### レイアウト

レンダリング ツリーが出来たので、いよいよ画面に表示したいところですが、端末のビューポート内での要素の正確な位置とサイズはまだ分かりません。

ページ上の各オブジェクトの正確なサイズと位置を算定するために、

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


等のレイアウト情報の計算を行います。

![image (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204455/ab324d2c-087f-a074-7c2c-3df9437de83a.png)





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

レイアウト情報の算出の算出が終わったので、いよいよ画面に描画です。

レイアウトツリーが画面上のピクセルに変換されます。

これでやっと画面上にページが表示されました！


## まとめ

アドレスバーにURLを入力してから、表示されるまでたくさんの処理があります。

こんなにたくさんの処理があるのに一瞬でページを表示してくれるブラウザって素敵だと思いませんか？

Webを見ているときに起きていることを理解してみると、普段何気なく書いてるJavaScriptのコードだったり、HTMLやcssの理解が深まる気がしています。


ブラウザの仕組みについて考えてみることで、フロントエンド開発で本当に大切なことが見えてくるような気がして、私はブラウザについて考えるのが好きです。



--- 
参考

- [Webフロントエンド ハイパフォーマンス チューニング](https://www.amazon.co.jp/exec/obidos/ASIN/4774189677/hatena-blog-22/)
- [ポートとソケットがわかればインターネットがわかる](https://www.amazon.co.jp/%E3%83%9D%E3%83%BC%E3%83%88%E3%81%A8%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88%E3%81%8C%E3%82%8F%E3%81%8B%E3%82%8C%E3%81%B0%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%8D%E3%83%83%E3%83%88%E3%81%8C%E3%82%8F%E3%81%8B%E3%82%8B%E2%80%95%E2%80%95TCP-IP%E3%83%BB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E6%8A%80%E8%A1%93%E3%82%92%E5%AD%A6%E3%81%B3%E3%81%9F%E3%81%84%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AB-Software-Design-plus/dp/4774185701)
- https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction?hl=ja