公開日: 2014 年 3 月 31 日
ブラウザでは、ページをレンダリングする前に、DOM ツリーおよび CSSOM ツリーを構築する必要があります。そのため、HTML と CSS の両方ができるだけ早くブラウザに提供されるようにする必要があります。
概要
- バイト→文字→トークン→ノード→オブジェクトモデル。
- HTML マークアップはドキュメント オブジェクト モデル(DOM)に変換され、CSS マークアップは CSS オブジェクト モデル(CSSOM)に変換されます。
- DOM と CSSOM は独立したデータ構造です。
- Chrome DevTools の [Performance] パネルでは、DOM と CSSOM の構築コストと処理コストをキャプチャして調査できます。
ドキュメント オブジェクト モデル(DOM)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
</body>
</html>
できるだけ単純なケースから始めましょう。テキストと画像 1 枚のシンプルな HTML ページです。ブラウザが、このページをどのように処理するのか見ていきます。
- 変換: ブラウザは、ディスクまたはネットワークから HTML の未加工のバイトを読み取り、ファイルに指定されているエンコード方法(UTF-8 など)に基づいて個々の文字に変換します。
- トークン化: ブラウザは、文字列を W3C HTML5 標準で指定されている個々のトークン(
<html>
、<body>
など)に変換します。山括弧で囲まれた文字列も同様です。各トークンには、固有の意味と、一連のルールがあります。 - 字句解析: 発行されたトークンは、プロパティとルールを定義する「オブジェクト」に変換されます。
- DOM の構築: 最後に、HTML マークアップは異なるタグ間の関係を定義するため(一部のタグは他のタグ内に含まれます)、作成されたオブジェクトは、元のマークアップで定義された親子関係もキャプチャするツリー データ構造でリンクされます。ドキュメントの表現全体が構築されるまで、HTML オブジェクトは body オブジェクトの親であり、body は paragraph オブジェクトの親です。
このプロセス全体の最終的な結果が、このシンプルなサンプルページのドキュメント オブジェクト モデル(DOM)です。ブラウザでは以降このページを処理する際に、必ずこの DOM を使用します。
ブラウザは HTML マークアップを処理するたびに、バイトを文字に変換し、トークンを識別してトークンをノードに変換し、DOM ツリーを構築するという上記の全ステップを行う必要があります。この一連のプロセスには時間がかかることがあります。特に処理する HTML が大量の場合は、その傾向が顕著です。
Chrome DevTools を開き、ページの読み込み時にタイムラインを記録すると、このステップを実行するために実際にかかった時間を確認できます。上記のサンプルの場合、一連の HTML を DOM ツリーに変換するのに 5 ms 近くかかっています。ページが大きい場合は、このプロセスにかかる時間が大幅に長くなる可能性があります。スムーズなアニメーションを作成するとき、ブラウザが大量の HTML を処理しなければならない場合に、この仕組みによってボトルネックが生じるおそれがあります。
DOM ツリーは、ドキュメント マークアップのプロパティおよび関係性をキャプチャしていますが、各要素がレンダリング時にどのように表示されるのかは示していません。こは CSSOM の範疇です。
CSS オブジェクト モデル(CSSOM)
ブラウザで、この基本的なページの DOM を構築している間に、ドキュメントの <head>
で <link>
要素に遭遇しました。この要素は、外部の CSS スタイルシート style.css
を参照しています。ブラウザは、ページのレンダリングにはこのリソースが必要であると想定して、このリソースに対するリクエストを即座にディスパッチします。これにより、次のコンテンツが返されます。
body {
font-size: 16px;
}
p {
font-weight: bold;
}
span {
color: red;
}
p span {
display: none;
}
img {
float: right;
}
HTML マークアップの内部で直接スタイルを宣言することもできますが(インライン)、CSS を HTML から独立させておくことで、コンテンツとデザインを切り離して扱うことができます。デザイナーは CSS で作業をして、デベロッパーは HTML に集中するといったことが可能です。
HTML と同じく、取得した CSS ルールをブラウザで認識および処理できる状態に変換する必要があります。このため、HTML ではなく CSS のために HTML のプロセスを繰り返します。
CSS のバイトが文字、トークン、ノードへと変換されていき、最終的に「CSS オブジェクト モデル」(CSSOM)というツリー構造にリンクされます。
CSSOM はなぜツリー構造をしているのでしょうか。ページ上のオブジェクトの最終的なスタイルセットを計算する際、ブラウザはまず、そのノードに適用される最も一般的なルールから開始します(たとえば、body 要素の子要素の場合は、すべての body スタイルが適用されます)。次に、より具体的なルールを適用することで、計算されたスタイルを再帰的に絞り込み、ルールを「下方にカスケード」します。
具体的に説明するために、前述の CSSOM ツリーについて考えてみましょう。body 要素の内部に配置されている <span>
タグに囲まれたテキストは、フォントサイズ 16 ピクセルの赤色のテキストになります。font-size
ディレクティブが、body
から span
にカスケード ダウンされています。ただし、span
が段落(p
)タグの子である場合、そのコンテンツは表示されません。
また、前述のツリーは完全な CSSOM ツリーではなく、スタイルシートでオーバーライドすると決めたスタイルだけが表示されています。各ブラウザには、「ユーザー エージェント スタイル」というデフォルトの一連のスタイルが用意されています。独自のスタイルを指定していない場合は、このスタイルで表示されます。スタイルを指定すると、このデフォルトがオーバーライドされます。
CSS 処理にかかる時間を調べるには、DevTools でタイムラインを記録し、[Recalculate Style] イベントを探します。DOM 解析とは異なり、タイムラインには個別の [Parse CSS] エントリはありません。代わりに、この単一イベントの下で、解析、CSSOM ツリー構築、計算済みスタイルの再起計算がキャプチャされます。
この小さなスタイルシートは、処理に最大 0.6 ms かかり、ページ内の 8 つの要素に影響を与えています。ささいな影響とはいえ、ゼロではありません。ところで、この 8 個の要素はどこにあったのでしょう。CSSOM と DOM は独立したデータ構造です。そうです、ブラウザに重要なステップが隠されているのです。次に、DOM と CSSOM をリンクするレンダリング ツリーについて説明します。