HTMLとCSSのパフォーマンス改善について

今回は、HTMLとCSSのパフォーマンスを向上させる小技と豆知識についてです。

しかしその前に、ブラウザがページをレンダリングする方法について知る必要があるでしょう。そこには[HTML]
(https://en.wikipedia.org/wiki/HTML)や[CSS]
(https://en.wikipedia.org/wiki/Cascading_Style_Sheets)、[JavaScript]
(https://en.wikipedia.org/wiki/JavaScript)、そしてブラウザがWebページをエンドユーザーに表示する際に必要な手順のすべてが含まれます。

#クリティカル・レンダリング・パス

[クリティカル・レンダリング・パス]
(https://css-tricks.com/understanding-critical-rendering-path/)は、5つのステップから成っています。図解すると分かりやすいでしょう：

![](https://entry.anypicks.jp/wp-content/uploads/2019/05/1.jpg)

見たままですが、各ステップについて詳しくご説明しましょう。

#DOMとCSSOMの構築

ほとんどのページはHTML、CSS、およびJavaScriptで構成されています。Webブラウザは、ユーザーに何をどのように表示するかを知る必要があります。そのため、ネットワークを介して送信されたHTMLページを解読し、ドキュメントオブジェクトモデル（DOM）を構築します。また、HTMLタグ（`<p></p>、<div></div>、<h1></h1>`）」を読み、それらをトークンに変換します。これらのトークンは同時にノードへと変換されます。

各トークンの開始タグと終了タグを順番に処理することで、ブラウザはそれらの階層（親と子）を確立します。

これは見かけよりもずっと簡単です。上の図で、DOMはすべてのノード（枝）とその子（葉）を持った一本の木のようなものと想像してください。この木は、HTMLを構築するためのノードの依存関係をすべて含んでいます：

　　　　![](https://entry.anypicks.jp/wp-content/uploads/2019/05/2-3.jpg)
　　　　　　　　　　　　　　　　[W3C website]
(https://www.w3schools.com/js/pic_htmltree.gif)より

DOMはマークアップ全体を表し、ブラウザによって徐々に構築されます。HTML5以降、ブラウザはページ全体が一度にブラウザに送信されるのを待つ代わりに、HTMLストリーミングに対応するようになりました。これの素晴らしいところは、情報を受け取ると同時にレンダリングを実行できる点です。

DOMを構築した後、ブラウザはCSSオブジェクトモデル（CSSOM）を構築するためにページ内で参照されている、あらゆるスタイルを調べ上げます。CSSOMは、文書に適用する必要のあるスタイルの規則を表しています。これはDOMがどう構築されたかに非常に似ていますが、この過程でそれぞれの子要素は、その親要素に適用されたスタイルを継承します ― cascading style sheets（一繋がりのスタイルシート）という名前はそのためです。

スタイルの部分的な処理は不可能なため、すべてのスタイルの規則が送信されるまで、ブラウザはどのスタイルの規則がどのノードに適用されているかを推測できません。これが、CSSがレンダリングをブロックする要素となる理由です。

DOMとCSSOMの混在は、レンダーツリーと呼ばれます。レンダーツリーは、すべてのノードとその依存関係、およびそれらに適用されるCSSルールすべてを含んでいます。

#レンダーツリー

すべてのノードを構築した後、ブラウザはどのノードをページ上に表示するのかを知る必要があります。レンダーツリーは、ページ上に表示される可視コンテンツそのものです。

ブラウザはルートから起動し、DOMとCSSOMからすべての可視ノードをコピーします。実行される手順は、おおよそ以下のとおりです：

- ルートから起動し、各可視ノードをたどる
  - メタタグやリンクなど、可視ノードではないものもあります。
  - display: noneなどのCSSを使って、隠されているノードもあります。
  
- 各ノードに対して一致するCSSルールを見つけて、適用する
- 可視ノードとそのコンテンツ、スタイルを出力する

![](https://entry.anypicks.jp/wp-content/uploads/2019/05/3-7.png)

#レイアウト

すべてのノードにスタイルを設定したら、次はそれらを画面上のどこに配置するか、つまりレイアウトを考える番です。初めに必要なのはブラウザのウィンドウサイズです。これにより各要素の位置と大きさが特定されるため、これは以降のすべての計算の基礎となります。

このステップの出力は、その正確な位置とサイズに加え、マージン、パディング、ボーダーなどを捉えた[ボックスモデル]
(https://www.w3schools.com/css/css_boxmodel.asp)です。

このステップは、サイズを変更するたび、またモバイル端末で横長と縦長の表示を切り替えるたびに繰り返されます。

#描画

この最後のステップでは、画面上にピクセルが描画されます。ブラウザのタスクが多く、多少の時間を要することがあります。所要時間は、ウィンドウサイズ、各ノードに適用されるスタイル、端末として使用されるハードウェアの種類などにより異なります。

完了すると、ようやくページがビューポートに表示されます。

#適用

クリティカル・レンダリング・パスについてはこれで十分。それでは、HTMLとCSSをいじってWebパフォーマンスを向上させる方法を見ていきましょう。

#クライアントにHTMLを送信する

クライアントにHTMLを送信するときは、最後に1回だけではなく、頻繁にバッファを書き出すようにしてください。そうすることで、HTMLの情報群を受け取る前から、ブラウザはそれらを解析できます。これによりパフォーマンスが向上します。

例として、HEADタグを受け取ると、ブラウザはその他のHTML情報を受け取る間に、他のアセットに対する要求を送信できます。

#サイズを縮小する

ミニフィケーションおよび圧縮は、読み込みサイズを縮小、HTTP応答時間を短縮し、結果として読み込み時間の短縮に繋がるため、非常に重要です。

これらのテクニックと正しいキャッシング戦略を組み合わせることで、Webパフォーマンスを大幅に向上させることが可能です。

#CSSを素早く送り、最小限に抑える

見てきたとおり、CSSはレンダリングをブロックする要素なので、できるだけ早くクライアントに送信する必要があります。また、初めのページの読み込みに必要なCSSの量を考えて、メインファイルから未使用のCSSを抽出し、必要に応じて後で別々に読み込みます。

パフォーマンス向上のため、プレローディング・プレキャッシングも活用できます。これらは、[Googleが2016年に導入したPRPLパターン]
(https://developers.google.com/web/fundamentals/performance/prpl-pattern/)の一部です。

もう一つのテクニックは、開発者たちの間でそれほど頻繁には使われていませんが、コンテンツのbody内にlinkタグを使用することです。たとえHTML内に記述されたstyleタグであっても、最小限のCSSのみをクライアントに送信し、残りは後から必要に応じて読み込むことでレンダリング時間を短縮できるため、非常に便利です。

#まとめ

要するに、関連するものを最小限に抑えて、素早く最適に送信すること。転送を高速化するため未使用のCSS規則を減らし、ミニフィケーションと圧縮を活用することです。

この投稿が少しでもお役に立ちますように。それでは、次回をお楽しみに😉

#参考

- Webパフォーマンス関連：[Google Developers]
(https://developers.google.com/web/fundamentals/performance/why-performance-matters/)
- クリティカル・レンダリング・パスの理解：[CSS Tricks]
(https://css-tricks.com/understanding-critical-rendering-path/)

#関連記事

[CSSを調べてコピーする簡単で最速なツール「CSS Scan 2.0」](https://www.anypicks.jp/news_resources/1641)

[観測史上最速！GoogleスプレッドシートだけでAPIが作れる「Sheetson」](https://www.anypicks.jp/news_resources/1769)

[GitHubのリポジトリだけでAPIが作れる「APIs With GitHub」](https://www.anypicks.jp/news_resources/1406)

この記事は、AnyPicksマガジンからの再掲載です。原文は[こちら]
(https://entry.anypicks.jp/improve-html-and-css-performance/)です。