はじめに
前回の記事の範囲まででDOMツリーが構築された。が、一つ目の記事でも書いたようにDOMと画面描画は完全に独立している。
ブラウザは、DOMツリーを元に色々な段階経て、最終的にピクセルを画面に描画する。
この色々な段階をレンダリングパイプラインと呼ぶ。
てことでレンダリングパイプラインを学ぶ!
レンダリングとは
コンピュータが持つ数値データや抽象的な情報を、人間が視覚的に理解できる画像や映像、音声などに変換・生成する処理のこと
レンダリングパイプラインと呼ばれる理由
工場のライン作業みたいに、各段階を順番に通って完成するから
DOM => CSSOM => Render Tree => Layout => Paint => Composite
↓ ↓ ↓ ↓ ↓ ↓
原料 加工 組み立て 配置 塗装 完成
レンダリングパイプラインの全体像
DOM => CSSOM => Render Tree => Layout => Paint => Composite
① ② ③ ④ ⑤
各段階で、異なる処理が行われる。
①CSSOMの構築
DOM = HTMLの構造をツリーにしたもの
CSSOM = CSSの構造をツリーにしたもの(CSS Object Model)
ブラウザはHTMLをパースしながら同時にCSSも解析してCSSOMを作る。
body { font-size: 16px; }
.container { width: 100%; }
.container p { color: blue; }
body
├─ font-size: 16px
└─ .container
├─ width: 100%
└─ p
└─ color: blue
CSSの読み込みが終わるまでレンダリングは始まらない。
なぜか?
=> 一瞬スタイルなしで表示される(FOUC)を防ぐため
なのでCSSは<head>内でできるだけ早く読み込む方がいい。
// スタイルシートの取得
const styleSheets = document.styleSheets;
// 特定のルールへのアクセス
const rules = styleSheets[0].cssRules;
console.log(rules[0].selectorText); // "body"
console.log(rules[0].style.fontSize); // "16px"
②レンダーツリー(Render Tree)の構築
レンダーツリー = DOM + CSSOM を合体させたもの
ただし、「画面に表示されるものだけ」 がレンダーツリーに含まれる。
P1:レンダーツリーに含まれない要素
以下の要素は「画面に表示されるものでない」のでレンダーツリーに含まれない。
-
<head>とその内容(<title>、<meta>、<script>など) -
display: noneが指定された要素 -
<script>タグ -
<style>タグ
P2:display: noneとvisibility: hiddenの違い
<div style="display: none;">レンダーツリーに含まれない</div>
<div style="visibility: hidden;">レンダーツリーに含まれる</div>
-
display: none:レンダーツリーに含まれない&レイアウトに影響しない。 -
visibility: hidden:レンダーツリーに含まれる&レイアウト上の空間を占める。
P3:擬似要素
::beforeや::afterなどの擬似要素は、DOMには存在しないがレンダーツリーには含まれる。
.item::before {
content: "• ";
}
この擬似要素はDOMノードとしては存在しないが、レンダーツリーではレンダーオブジェクトとして存在する。
③レイアウト(Layout / Reflow)
レイアウト(Layout / Reflow) = レンダーツリーが構築された後、各要素の正確な位置とサイズを計算する処理のこと。
P1:レイアウトで計算されるもの
- 要素の幅と高さ
- 要素の位置(x, y座標)
- マージン、パディング、ボーダーの寸法
- フロート、フレックスボックス、グリッドによる配置
P2:ボックスモデル
すべての要素はボックスとして扱われる。
┌─────────────────────────────────┐
│ margin │
│ ┌───────────────────────────┐ │
│ │ border │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌───────────────┐ │ │ │
│ │ │ │ content │ │ │ │
│ │ │ └───────────────┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └───────────────────────────┘ │
├─────────────────────────────────┤
│ margin │
│ ┌───────────────────────────┐ │
│ │ border │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌───────────────┐ │ │ │
│ │ │ │ content │ │ │ │
│ │ │ └───────────────┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
④ペイント(Paint / Repaint)
ペイント(Paint / Repaint) = レイアウトが完了した後、実際にピクセルを描く(色をつける)
P1:ペイント順序
背景色 => 背景画像 => ボーダー => 子要素 => アウトライン
※なのでbackground は border より後ろに来ない
P2:z-indexとスタッキングコンテキスト
要素の重なり順は、「z-indexで決まる」のではなく「スタッキングコンテキストによって決まる」 と考える。(何もない場合普通はHTMLの後に書いた方が上)
.parent {
position: relative;
z-index: 1; /* 「新しいスタッキングコンテキストを作成」と考える。 */
}
parent-a (z-index: 1)
└─ child-a (z-index: 9999) ← どんなに大きくても
parent-b (z-index: 2) ← 親が勝ってるので
└─ child-b (z-index: 1) ← こっちが上に来る
子は親のスタッキングコンテキストの中でしか戦えない。
スタッキングコンテキストを作成する条件の一部
-
positionがrelative、absolute、fixed、stickyで、z-indexがauto以外 -
opacityが1未満 -
transformがnone以外 -
filterがnone以外 -
will-changeで特定のプロパティを指定
メモ⬇️:スタッキングコンテキストについてのわかりやすい記事
https://ics.media/entry/200609/
⑤コンポジット(Composite)
コンポジット(Composite) = レイヤーを合成して最終的な画面を作る段階.。
各レイヤーを個別に描画して最後にGPU(画像処理が得意&並行処理)を使って合成する。
レイヤーが作成される条件
以下の条件を満たす要素はレイヤーになる。(これは日本語あってるのか)
-
transformプロパティが使用されている -
opacityが1未満 -
will-changeプロパティが指定されている -
<video>、<canvas>要素 position: fixed- 3D変換(
transform: translate3d()など)
┌─────────────────────┐
│ ヘッダー(固定) │ ← スクロールしても動かない
├─────────────────────┤
│ │
│ コンテンツ │ ← スクロールで動く
│ コンテンツ │
│ コンテンツ │
│ │
├─────────────────────┤
│ フッター(固定) │ ← スクロールしても動かない
└─────────────────────┘
.header {
position: fixed; /* 固定 => レイヤー誕生 */
top: 0;
}
.footer {
position: fixed; /* 固定 => レイヤー誕生 */
bottom: 0;
}
レイヤー3: ヘッダー(固定)
レイヤー2: フッター(固定)
レイヤー1: コンテンツ(スクロールする部分)
↓ 合成 = コンポジット(Composite)
画面に表示
レイヤーの意義 = 再計算、再描画される箇所を減らすことができる
補足:レンダリングパイプラインの再実行
DOMやCSSが変更されるとパイプラインの一部または全部が再実行される。
Reflow(レイアウトの再計算)が発生する変更
- 要素の追加・削除
- 要素のサイズ変更(width、height、padding、margin、border)
- フォントサイズの変更
- テキスト内容の変更
- ウィンドウのリサイズ
- スクロール位置の取得
Repaint(ペイントからの再実行)のみが発生する変更
- 背景色の変更
- テキスト色の変更
- 影の変更
- 可視性の変更(visibility)
Compositeのみ発生する変更
-
transformによる変形 -
opacityの変更
まとめ(感想)
知らないって怖いなと思った。
TODOクリティカルレンダリングパス勉強する
https://web.dev/learn/performance/understanding-the-critical-path?hl=ja
参考文献
- Rendering Performance - web.dev
- How Browsers Work - HTML5 Rocks
- CSS Object Model (CSSOM) - W3C
- Compositing in Blink and WebKit - Chromium
- MDN Web Docs - CSS Object Model - Mozilla
- The Pixel Pipeline - Google Developers