レンダリングの理解が曖昧なままReactを使っていた自分をぶん殴りたい


エンジニアのみなさん、レンダリングとは何か説明できますか?

「答えられるに決まってんだろ!わかんねぇ奴はエンジニアやめちまえ!!」って方がいたらすみません。気持ちはわかります!


......でもごめんなさい。実は私、答えられませんでした。

厳密にいうと、レンダリングという言葉が文脈によって意味が変わってくることをこれまでそんなに意識していませんでした。業務で使っているReactもフワフワした理解のまま手を付けていたということです。


こりゃ勉強しなおさなきゃだめだわ...と思い、このたびブラウザのレンダリングReactのレンダリングがそれぞれどのようなものであるのかを再学習しました。

ブラウザのレンダリング

ブラウザのレンダリングは、アドレスバーにURLを打ち込んでからWebページが表示されるまでの一連の流れのことをいいます。
この一連の流れは大きく4つのフレームに分けられ、さらに各フレームで処理が分けられます。

  1. Loading
  2. Scripting
  3. Rendering
  4. Painting

1. Loading


LoadingはURLからHTMLを読み込んで、さらにレンダリングに必要なリソースを読み込んで解釈する工程です。この工程はさらにDownloadとParseにわけられます。

Download

リソースをサーバからダウンロードします。リソースには以下のものが含まれます。

  • HTMLファイル
  • CSSファイル
  • JavaScriptファイル
  • 画像ファイル(JPG, PNG, GIF, SVG)

Parse

Parseは日本語で「構文解析」という意味です。ただ、ここでいうParseとは、ダウンロードしたリソースを構文解析して、レンダリングエンジンの内部表現に変換するまでの処理を表します。

例えば、HTMLであればDOMツリー、CSSであればCSSOMツリーに変換されます。


2. Scripting


ScriptingはJavaScriptを実行する工程です。実行といっても、ブラウザがJavaScriptのコードをそのまま処理するわけではありません。HTMLがLoadingのParse処理で構文解析されるように、JavaScriptについても同様の構文解析処理が必要になります。


HTMLやCSSではレンダリングエンジンによって構文解析が行われましたが、JavaScriptについてはJavaScriptエンジンがその役割を受け持ちます。JavaScriptエンジンによって、コード→トークン列→ツリー(抽象構文木)に変換され、最後にツリーは実行可能な形式にコンパイルされます。

3. Rendering


Renderingはレイアウトツリーを構築する工程です。Renderingという名前で紛らわしいのですが、こちらはあくまでブラウザのレンダリング工程の一部です。

Calculate Style

Calculate StyleはDOMツリー内のすべてのDOM要素に対して、どのCSSプロパティが該当するのかを計算する処理です。このDOM要素に対するCSSルールの適用は、レンダリングエンジンが行っています。

Layout

LayoutはCalculate StyleでDOM要素に該当するCSSプロパティを算出したあとに、DOMツリー内のノードのレイアウト情報を計算する処理です。

レイアウト情報には、margin, padding, width, height, positionなどが該当します。

4. Painting


Paintingでは、Rendering工程のLayoutで計算したDOMツリーのレイアウト情報をもとに描画を行います。

Paint

Paintは2Dグラフィックエンジン向けの命令を生成する処理です。このグラフィックエンジンはブラウザの実装によって異なり、レンダリングエンジンによって組み込める種類が変わってきます。

Rasterize

Rasterizeは生成された命令を用いてピクセルに描画する処理です。レイヤー単位で一枚ずつ描画されます。

レイヤーは重なって表示されるコンテンツ(透過して背後が表示されるようなコンテンツなど)がある場合に生成されます。例えば、以下のプロパティが適用される場合です。

{
  position: fixed;
  position: absolute;
  transform: translate3d(0px, 0px, 0px);
  opacity: 0.5;
}

Composite Layers

Composite LayersはRasterizeで描画したレイヤーを合成して最終的な結果を生成する処理です。

以上がレンダリングの一連の流れです。どの工程でどんなことを行っているのか?を把握しておくと、パフォーマンスを意識したコーディングができるわけですね。

Reactのレンダリング

Reactのレンダリングは以下の工程を表します。
1. 現在のPropsとStateをもとにルートから更新が必要なコンポーネントを探す。
2. 該当するコンポーネントがレンダリング出力(JSX)を返す。
3. 変更箇所の差分を計算し、変更されたDOMのリストを収集する。

計算された変更のDOMへの適用はレンダリング後に行われます。この工程はコミットと呼ばれます。

レンダリングとコミットで何が行われているかがわかると、Reactのライフサイクルメソッド(または副作用)がどのようなタイミングで行われるのかを理解することができます。


参考: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

おわりに

レンダリングを学びなおしてみて、これまで曖昧だった知識がようやく固まりました。基礎を理解できていないままReactのようなフレームワークを使っていたことを反省します...

フロントエンド関連の参考書ではレンダリングという言葉が多用されますが、文脈でレンダリングの主体が何であるのかを意識して読むように気をつけたいと思います。

参考文献

Webフロントエンドハイパフォーマンスチューニング 久保田 光則 著
React公式ドキュメント