1. goemp

    No comment

    goemp
Changes in body
Source | HTML | Preview
@@ -1,213 +1,158 @@
# 概要
webサービスを公開するにあったて必ず使われることになるのがブラウザです。ブラウザがユーザーにwebページを表示する仕組みを理解することで、フロントエンド開発に役立てたり、ページ表示までのレスポンスの改善などに役立てていきたいと思い、今回ブラウザのレンダリングの仕組みの基本事項についてまとめました。
## レンダリングの流れ
ユーザーがwebブラウザにURLを入力すると、ブラウザはURLを元に指定のサーバーにTCP/IPプロトコルに基づいてリクエストを送ります。その後サーバはクライアントに対してレスポンスします。以降のレスポンスとして受け取るHTML,CSS,Javascriptをどう処理して画面に表示するのかをレンダリングと定義して、その処理の流れについてみていきます。(この工程はcritical rendering pathと呼ばれています)
![progressive-rendering.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/0f378229-031d-7314-a828-4e9945530fb8.png)
https://developers.google.com/web/fundamentals/performance/critical-rendering-path
-![レンダリングの流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/8db46249-aa8b-eb64-e0ba-0d109f555235.png)
+![レンダリングの流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/78cd1ed1-dc53-5c23-e5c5-9737e38b00e4.png)
+
+
ブラウザがWebページをレンダリングする仕組みは上のような一連の流れになっています。以下でその一つ一つの工程の内容をみていきます。
## オブジェクトモデルの構築
ブラウザはページを描画する前に、DOMとCSSOMツリーを構築する必要があります。
このDOMとCSSOMツリーを構築する工程は「オブジェクト構築モデル」といいます。
バイト→文字列→トークン→ノード→オブジェクトモデル(DOM/CSSOM)
という流れでオブジェクトモデルは構築されます。
![constructingObjectModel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/fc0503ad-2abc-992a-0405-3e78b1252e4c.png)
参考:https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model
### DOMツリーの構築
![dom-tree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/a40efe52-e694-83b5-ca1c-cda5a45de3b0.png)
#### 変換
ブラウザはディスクやネットワークからHTMLのバイトを読み取り、utf8などの文字コードに応じて\<html>や\<head>のような文字列に変換します
#### トークン化
取得した文字列をトークンに変換します。[W3C HTML5 standard](https://html.spec.whatwg.org/multipage/)によって規則が定められています。
![スクリーンショット 2019-11-27 23.17.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/6ca56341-6994-13ec-b8d1-0a1ea18d74ec.png)
#### 字句解析
トークンは\<html>や\<head>といったオブジェクトに変換されます。
![スクリーンショット 2019-11-27 23.19.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/e218ad05-c29b-fb6e-43ba-c88ca0e88a1b.png)
#### DOMの構築
字句解析で作られたオブジェクトはオリジナルのマークアップの定義に基づいた親子関係をもつ木構造に整形されます。
\<html>オブジェクトは\<body>オブジェクトの親で、\<body>は\<p>の親でといったようなHTMLのマークアップでみる構成になります。
この最後のプロセスの最終的な成果物が描画するページのDOMとなります。
### CSSOMツリーの構築
![cssom-tree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/a3019e6b-676c-270f-ae96-b67dd573721c.png)
ブラウザでDOMを構築している際にドキュメントのheadタグで外部のcssスタイルシートを参照しているlinkタグに遭遇すると、ブラウザはページのレンダリングにこのリソースが必要であると解釈してこのリソースに対するリクエストを即座にディスパッチし、CSSを受け取ります。
ブラウザがCSSを解釈して処理できるようにするために、受けっとたCSSをHTMLのDOMツリー構築の時と同じように処理していきます。
-## JavaScriptの実行
-各種リソースを読み込んだ後は、JavaScriptエンジンによってJavaScriptのコードが解析、実行されます。
-まずJavaScriptのコードが字句解析されトークンとなり、次に構文解析され抽象構文木となり、最後にコンパイルされて実行可能なファイルとなり実行されます。
-
## レンダリングツリーの構築
-コンテンツを記述したDOMツリーとドキュメントに適用するスタイルルールを記述したCSSOMツリーを結合することでレンダリングツリーを構築する必要があります。レンダリングツリーは各表示要素のレイアウトを計算するために使用され、画面にピクセルをレンダリングするペイント処理の入力となります。
+DOMツリーとCSSOMツリーを組み合わせることでレンダツリーをります。レンダツリーは個々の要素のレイアウトの計算し、またピクセルを画面に描画するプロセスである「ペイント」の工程が必要とする値を生成します。
-レンダリングツリー構築の手順として、DOMツリーの各要素に対してどのCSSプロパティがマッチするかを計算します。CSSのルールセットにはh1やpのようなCSSセレクタと、widthやcolorのようなCSSプロパティがあり、最初にCSSセレクタによってDOMツリーの要素とCSSルールのマッチングを行います。その後、各DOM要素にどのCSSプロパティがマッチングするかを計算します。
+#### レンダーツリーの生成
+DOMツリーとCSSOMツリーを合成してレンダーツリーを生成します。ページの描画に使われる全てのDOMと個々のオブジェクトに使われるCSSをそれぞれ組み合わせます。
![render-tree-construction.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/b0de712a-37f5-32fb-dcea-2b5d8862568a.png)
+レンダーツリーを構築するために、ブラウザは大まかに以下の工程に従います
-#### CSSセレクタのマッチンング
-レンダリングエンジンはCSSセレクタを右側から順に評価していきます
-
-ex)
-
-```css
-.name > ul > li > p {
- color: red;
-}
-```
-
-上のような場合、レンダリングエンジンはページ内の全ての要素に対して次のように判定します
+1. DOMツリーのルートからはじめて、個々のノードをトラバースしていきます
+ - scrpitタグなど画面の描画に直接関係ないいくつかのノードは除かれます
+ - いくつかのノードはCSSにより`display none`などど非表示に指定されたものも除かれます(上の図の例でいうと\<span>にあたります
-1. DOMがpである
-2. pの親要素がliである
-3. liの親要素がulである
-4. ulの親要素のclass名にnameが含まれている
+2. それぞれのノードに対応したCSSOMをみつけて適用します
+3. CSSが適用された表示可能になったノードを除きます
-#### CSSプロパティのマッチング
-どのCSSプロパティがDOM要素に適用されるかをレンダリングエンジンが決定するための詳細度という仕組みがあります。詳細度には3つのレベルがあり、各CSSセレクタとの関連は次のようになっています
+これらの工程によりページの描画に必要な全てのノードとスタイル情報を含んだレンダツリーが作成されます。
-A. IDセレクタ
-B. クラスセレクタ、擬似クラスセレクタ(:first-child)、属性セレクタ([type=input])
-C. 要素セレクタ(div)、擬似要素セレクタ(::before)
+## レイアウト
+レンダリングツリーの構築まででどの要素が描画されてどんなスタイルが適用されるかが決まりました。この次に、デバイスのviewport内でそれら要素の厳密な位置やサイズを算出するレイアウトの工程が行われます(リフロートも言われます)
-各レベルの優先度は A > B > Cとなっていて1つでも上位レベルが含まれる場合はそれが優先され、同じレベルでは値が大きい方が優先されます。
-
-ex)
-
-```css
-// A=0, B=2,C=0
-.wrapper > .container {
- color: red;
-}
-
-// A=0, B=1, C=1
-div > .container {
- color: white;
-}
-
-// A=1, B=0,C=0
-#id {
- font-size: 20px;
-}
-
-// A=1, B=0,C=0
-#id {
- font-size: 30px;
-}
-
-// A=1, B=0, C=1
-#id::before {
- content: 'user';
-}
-```
-
-上の例で2つの`container`クラスが同じ要素に対して修飾されている場合、詳細度から `color:red`の方が優先されます。また詳細度が同じ場合は後に定義されたCSSルールセットが適用されますそのため上の例の`#id`の要素には`font-size:30px`が適用されます。
-
-## レンダリングツリーのレイアウト
-レイアウトの工程では端末ビューボード内での各ノードのレイアウト情報を算出しますレイアウト情報には要素の大きさやmargin/padding、x/y/z軸の位置などが含まれます。
-レンダリングエンジンはレンダリングツリーのルート要素から順番に各要素が持つCSSプロパティを元にレイアウト情報を決めていきます。
+ページ上での個々の要素の正確なサイズや位置を求めるために、レンダーツリーのルートからブラウザはトラバースします。
ex)
```html
<!DOCTYPE html>
<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>
```
-上のようなHTMLの場合、`viewport`がデバイスのサイズで倍率が1倍ということに決まります。
-ネストされた2つのdivがあり、1つ目のdivviewportの幅の50%になり、2つ目のネストしたdivの幅は親のdivの50%(全体の25%)になります。
+上のページのコンテンツは2つのネスとされたdiv要素をもち、最初のdivviewportの幅の50%に位置し、2つ目のdivは親要素のdivの50%の位置となります。
![layout-viewport.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/7aea1607-b729-279d-2cc4-076d4a2e1ce1.png)
+レイアウトプロセスで出力されるものを「ボックスモデル」といいます。ボックスモデルはviewport内での個々の要素の正確な位置とサイズを算出し、相対的な値を画面上でのピクセルの絶対値に変換します。
-## レンダリングツリーの描画
-webページの描画にはペイントとコンポジットの2つの工程があります。
-
-#### ペイント
-各レイヤごとにテキストや色、画像などをピクセルに書き込みます。
-1. 命令リストの作成
-2. ピクセルへの書き込み
-
-の2つの工程からなります。
+## ペイント
+最後に、レイアウトプロセスで得ることができた個々の要素のサイズと位置情報を最終的な正確なピクセル値へと変えるペイント(又はラスタライゼイション)の行程が行われます
-まず前の手順で構築したレンダリングツリーを元にグラフィックエンジンのための命令リストを作成します。命令リストでは「どのピクセルに何色を入れるのか」という命令が入っているため、その命令を元にグラフィックエンジンがピクセルの描画を行います。
-次にピクセルの書き込みを行います(ラスタライゼーション)。ピクセルの書き込みはレイヤ単位で行われ、position:abosolute;やopacityといったCSSプロパティなどz軸を考慮する必要のある要素が存在する場合は新しいレイヤが作成されます。
+![layout-timeline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274080/e4cd5fb1-ca3b-e025-19fa-8dd4c54496be.png)
+【Chrome DevToolsでの処理の経過の様子】
+https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction
-#### コンポジット
-ピクセルを書き出したレイヤを合成してレンダリング結果を出力します。
+レンダーツリーの構築、レイアウト、そしてペイントの各工程に要する時間はドキュメントのサイズ、適用されたスタイルそしてデバイスによって様々です。
+それぞれの工程の合計時間を最小化することでクリティカルレンダリングパスを最適化することができます。
## その他
### レンダリングエンジンとJavaScriptエンジン
ブラウザにはレンダリングエンジンとJavaScriptエンジンという2つのエンジンが動作しています。
**レンダリングエンジン**
HTMLやCSSなどを解析し、実際の画面に描画するためのもの。
レンダリングエンジンによってHTMLやCSSの解釈に差があるためデザインがブラウザによって崩れるという問題があります。
**JavaScriptエンジン**
JavaScriptを実行するためのエンジン。
【主なブラウザと各種エンジン】
|ブラウザ |レンダリングエンジン |JavaScriptエンジン |
|---|---|---|
|Google Chrome |Blink |V8 |
|Safari |Webkit |Nitro |
|IE |Trident |Chakra |
|Microsoft Edge |Blink |V8 |
|Mozilla Firefox |Gecko |SpiderMonkey |
|Opera |Blink |V8 |
Microsoft Edgeの新しいversion(Chromiumベース)が来年の1月15日にリリース予定です。
https://forest.watch.impress.co.jp/docs/news/1216492.html
# まとめ
ブラウザレンダリングの仕組みを各工程ごとにみることで、ページライフサイクルのイベントがどの工程で発生するのかをきちんと理解できるようになりました。レンダリングの仕組みの知識を元に、ブラウザの開発ツールを駆使して開発時にデバッグやパフォーマンスのボトルネックの分析、改善に役立てていきたいと思います。
# 参考資料
- [Constructing the Object Model](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model)
- [Software Design 2019年10月号](https://gihyo.jp/magazine/SD/archive/2019/201910)
- [UDACITY Website Performance Optimization
by Google](https://www.udacity.com/course/website-performance-optimization--ud884)
- [ブラウザレンダリング入門〜知ることで見える世界〜](https://qiita.com/Leapin_JP/items/caed57ec30d638e40728)
- https://html.spec.whatwg.org/multipage/
- https://developers.google.com/web/fundamentals/performance/rendering?hl=ja