1.はじめに
- 本記事は私の勉強中の思考の整理のために書き上げています。間違い等ありましたらご指摘いただけると幸いです。
- 参考文献は適宜添付と記事末尾にある通りです。
- 本文は思考過程をそのまま書いているので乱雑な文章になっている部分があります。
2.レンダリングってどういう意味?
レンダリングってそもそもどんな意味やねん。
辞書的な意味を押さえておこう!
render
- 〈文〉〔ある方法で~を〕表す、表現する、描写する
- 《コ》〔~を〕レンダーリングする、〔画像などのデータを〕実際に描画する、〔3次元のオブジェクトを〕2次元の画像にする◆データを解釈・計算して視覚的に表示すること。
レンダリングの辞書的な意味は「表す、描写する、データを視覚的に表示する」といった意味があるみたい。。
3.ブラウザレンダリング
ウェブページにアクセスするとこんな感じで画面が見える。
レスポンスで返ってきたHTMLがなぜこんなにも視覚的に見やすくなってるんだ!!
見やすくしてくれているのがブラウザレンダリングという仕組み!
すごい...
湧き出る疑問
ここでこんな疑問が湧き出てきました。
「どんなデータをどうやって視覚化しているんだろう。。。」
本記事ではこの疑問に答えるところをゴールとします!
4.ブラウザレンダリングの仕組み
それではこれからどんなデータがどうやって視覚化しているのか見ていきましょう!
HTMLファイルができるまで
Download
何はともあれサーバーからリソースをダウンロードしてこないといけない。
HTMLやCSS,JavaScript,静的コンテンツなんかをサーバーからゲット。
ここで早速「どんなデータ」という疑問の答えが半分わかります!
半分だけ答え = HTMLやCSS,JavaScript,静的コンテンツ
Parse
半分と言ったのには理由があります。
実は、まだこの時点ではさっき見たようなHTMLのソースコードの形になってないんです!
サーバーくんから送りたてほやほやのファイルは「0」と「1」でできたバイト形式です。
つまり、
「どんなデータ」という疑問の答えは「01でできたHTMLやCSS,JavaScript,静的コンテンツなどのリソース」となります。
これをHTMLとして解釈(解析)できる形にする必要があります。
それがParseです!!
(おおまかな流れ)
ブラウザレンダリングの仕組み
Bytes→Characters
Content-Typeで指定した文字コードの文字に変換するよ、のフェーズ。
ここで「0」「1」から文字に置き換わります!
Content-Typeでしていなかったら< meta>タグを見る。
<meta charset="utf-8">
どっちにも書いてなかったらHTMLの内容から自動判別(すごい)。
Characters→Tokens
最小文字列単位(Token)に変換するよ、のフェーズ。
開始タグ、終了タグ、文字列(HelloとかWorldとか)の単位。
Tokens→Nodes
トークンをノードに変換するよ、のフェーズ。
ノードってなんやねん!
ざっくりいうと「HTMLの要素や属性、テキスト」みたいな、、、
HTMLの基本構造(要素と属性)から記述方法、各タグの役割と意味まで解説。【ホームページ制作入門講座】
詳しくはこちら↓
Nodes→DOM
HTMLのノードで作られた階層構造=DOMツリーに変換するよ、のフェーズ。
DOMのちゃんとした説明
DOM ( Document Object Model ) とは、HTML文書をツリー状のデータ構造として扱い、それをプログラムから参照したり操作したりするためのデータ構造やインタフェース ( API ) を定義したものです。
ここでようやく「0」「1」のデータがかっこいいHTML形式の階層構造になったファイルになります。
CSSファイルができるまで
基本的にHTMLの時と同じ流れ。
ただしCSSでは、DOMツリーではなくCSSOM(CSS Object Model)というオブジェクトツリーができる。
階層構造がHTMLとCSSでは違うからね。
レンダリングエンジン
ここまでの仕事をしてくれているのがレンダリングエンジン。
レンダリングエンジンもブラウザごとにいろんな種類があるみたい。
ここディグっても楽しそう。。。
JavaScriptファイルができるまで
DOMツリー構築中にJavaScriptのコードやJavaScriptへの参照が入ると、一旦DOMツリー作成の手を止めて、JavaScriptファイルのダウンロードをする。
ダウンロードが済むとJavaScriptエンジンが
- 字句解析(トークンへ変換)
- 構文解析(ツリー構造構築)
- コンパイル
(この辺の詳しい話)
の順でJavaScriptソースコードを機械語に変換。
CPUが処理してJavaScriptが実行される。
(JavaScriptエンジンも色々あるなぁ、、)
これでようやくHTML,CSS,JavaScriptができた!!👏
レンダーツリー
完成したHTMLのDOMツリーとCSSのCSSOMがまだ別々の構造として保持されているので、この2つを合体させるフェーズ。
合体してできた構造をレンダーツリーと言う。
(このタイミングで、headとかhtmlみたいな視覚化に不要なタグは除外されるよ)
Layout
レンダーツリーだけだと表示内容(テキストとか)とその階層構造だけなので、
視覚化するために各ノードの位置やサイズを計算するよ、のフェーズ。
具体的なサイズはheadタグの
<meta name="viewport" ...>
の指定で決定。
Painting
疲れてきたかもしれないですが、この旅路も最後のフェーズになりました!!
ここまで諸々解析してきた内容を実際に描画するよ、のフェーズです。
ざっくり言うと、
- 描画する順番を決める(Paint)
- 解析内容をピクセル変換する(Composite)
の順で描画します。
(この辺の詳しい話)
答え合わせ
それでは本記事のゴールです!!疑問の答え合わせといきましょう!
湧き出る疑問
「どんなデータをどうやって視覚化しているんだろう。。。」
答え
どんなデータ = 「01でできたHTMLやCSS,JavaScript,静的コンテンツなどのリソース」
どうやって = 「各ソースファイルを
- ダウンロード
- 各ファイルのツリー構築
- レンダーツリー構築
- Layout
- Painting
という手順で視覚化している」
【余談】CSRとSSR
これまで説明してきたレンダリングには、
サーバーからソースデータを取ってきてクライアント側で処理するClient Side Renderingの場合とサーバー側でレンダリングするServer Side Renderigというのもあります。
CSR
SSR
どっちの方法にするかで
- SEOへの対応?影響?(ここ曖昧です、すみません。)
- 初回ローディングの速さ(サーバーでレンダリング済みなのでSSRの方が速い)
- サーバー側の負荷(サーバー側でレンダリングする分SSRの方が重い)
- Webサーバーの有無(JavaScriptをサーバー側で実行するのでSSRではWebサーバーが必要)
が変わってきます。
またCSRの場合はネイティブアプリの代わりとして利用できます(WebView利用?)。
(この辺の詳しい話)
5.モバイルアプリはなぜ表示できるのか
最近JetpackComposeの勉強をしていてこんな疑問が湧き出てきました。
「レンダリングエンジンとかってブラウザの中にあるのにモバイルアプリはどうやってレンダリングしているんだろう。。」
大きく2通りの方法があるっぽい。
独自レンダラを利用する場合(調査中)
ブラウザレンダリングはこっち。
アプリ内に独自のレンダリングエンジンが組み込んである。
それを利用してレンダリングする。
ブラウザではWebkitとかBlinkなどの独自レンダリングエンジンがある。
ゲームアプリとかで特定のグラフィックを描画するときとかも使うみたい(多分...)。
フレームワークがレンダリングエンジンとして機能する場合(調査中)
モバイルアプリではJetpackCompose,Fullerなどのフレームワークに組み込まれている独自のレンダリングエンジン(Android webkit,Flutter Engine)を利用する場合もある。
Flutterとかのクロスプラットフォームフレームワークなら、各レンダリングエンジンを使うよりフレームワーク独自のレンダリングエンジンを使った方が都合いい的な・・・・??(わからん。そもそもフレームワーク独自のレンダリングエンジンも独自レンダラじゃないの?)
Webkitとは
android.webkit|Android Developers
ネイティブ型
手順(jetpackComposeの場合)
Composition
どのUIを表示するのか決定するよ、のフェーズ。
コンポーズできる関数を実行、表示内容の親子関係から表示するノードのUIツリーを構築する。
コンポーズ可能な関数=@Composableアノテーションがついている関数。
Composition|Android Developers
Layout
作成したUIツリーの各ノードのサイズや位置を決定するよ、のフェーズ。
- 子ノードの測定
Rowを親ノードとするならImage,Columnが子ノード。
さらに子ノード:Columnの子ノード:Textがある場合は先にそっちを測定。 - 子ノードのサイズを親ノードに報告
子ノードをTextとするならColumnに、子ノードをColumnとするならRowに報告。
親ノードは子ノードのサイズを参考に自身のサイズを決定。 - 子ノードを配置
親ノードの位置に対して相対的に子ノードを配置。
Drawing
ツリーを実際に上から表示していくよ、のフェーズ。
背景色入れたり文字を出力したりなんやかんやする。
Jetpack Compose のフェーズ|Android Developers