ページスピードのチューニングその1:CSSとJSによるレンダリングブロックの仕組み
2016-09-30 16:44
page speed insightを見るとCSSのレンダリングブロックのアラートが出ることがあります。特にスマートフォンでページをチェックしたときにはこのメッセージがよく表示されると思います。
Googleのレファレンスを見ると、ファーストビューに必要なものはhead内でインラインで記述し、それ以外のものはあとから読み込むようにしてくださいとのことです。
今回、かなりこの問題の解決に苦労したので、いろいろ調べたことをまとめてみたいと思います。
基本的にはGoogleのベスト・プラクティス集である”Web Fundamentals“のうち、
「クリティカルレンダリングパス」という章を参考に見ていきたいと思います。
- そもそもレンダリングブロックとは
- javascriptとcssの依存関係
- ファーストビューのレンダリングブロック
こんな内容でお送り致します。
そもそもレンダリングブロックとは?
レンダリングブロックとは、言葉の通り、ブラウザのコンテンツの表示処理がブロックされる現象のようです。ブラウザがコンテンツを表示するためには基本的にHTMLの文字列をパース(解釈)する必要があります。
さらにスタイルシートが定義されていれば、それらもパースして、HTMLをパースして構築したDOMに紐付けなければなりません。逆にいうと、「ブラウザは、CSSOM の構築が完了するまで、処理済みコンテンツのレンダリングを保留します。」
CSSOM(CSS Object Model)とは、CSSに記述されているただの文字列をそれぞれの定義しているセレクタにもとづいてノードに分類し、ツリー状に構築したオブジェクトデータのことです。(参考)
ブラウザは、ページをレンダリングする前に、DOMツリーとCSSOMツリーを構築する必要がありますし、CSSOMツリーとDOMツリーを組み合わせて「レンダーツリー」を構築するようです。このレンダーツリーを使って、初めて描画処理が行われます。
だから「できる限り早くクライアントに渡し、最初のレンダリング時間を最適化する必要がある」のです。
例えば以下のような単純なHTMLで見てみましょう。
1 2 3 4 5 6 7 8 9 10 |
<!DOCTYPE html> <html> <head> <link href="small.css" rel="stylesheet"> </head> <body> <div class="blue">Hello, world!</div> </body> </html> |
small.cssには適当なスタイルが記述されています。
これをChromeのデベロッパーツールの”timeline”で見てみましょう。
おわかりだと思いますが以下の順序で処理をしていることが分かります。
- HTMLのダウンロード(水色のバー)
- ダウンロード後、HTMLの文字列をパース(DOMの構築)
- パースしたHTML内のlinkタグからCSSファイルをダウンロード開始
- CSSダウンロード後、CSS文字列をパース/CSSOMの構築(小さい青いバーです)
- 描画処理(紫のバーから緑のバーの一連の処理)
このように、CSSのパースが終わってからでないと描画処理が実行されていません。「表示処理はCSSの読み込みとCSSOMの構築に依存している」ことがわかります。これが最初の基本的なレンダリングブロックです。
javascriptとcssの依存関係
ブラウザはHTMLの文字列をパースしDOMを構築します。ところが、headタグ内にscriptタグがあるとブラウザは基本的にこのパース処理を中断してしまいます。ブラウザの制御権をパーサーからjavascriptエンジンに引き渡すからからです。
そして、さらにそのjavascriptを実行する際に、ダウンロードが始まっているCSSがある場合、そのCSSをダウンロードし終わるまでは、JSの実行もブロックされるようです。つまり、次のような依存関係が成り立っています。
表示処理->JSの実行->CSSの読み込み(CSSOMの構築)
CSSはさらにJSの実行をもブロックしてしまっているのです。だからなおさらCSSは早いタイミングで読み込まなければならないのです。
では実際にtimelineを見ながら確認していきましょう。コードは以下です。
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <html> <head> <link href="small.css" rel="stylesheet"> <script src="sample.js"></script> </head> <body> <div class="blue">Hello, world!</div> </body> </html> |
※ JSのブロッキングが分かりやすくなるように
small.cssはあえて、85KBと大きめなものを用意しています。
処理の順序
- HTMLのダウンロード
- ダウンロード後、HTMLの文字列をパース
- パースしたHTML内のlinkタグからCSSファイルをダウンロード開始
- 同様にJSファイルをダウンロード開始
- スタイルシートのダウンロード後、スタイルシートをパース
- パース完了後、JSを実行(Evaluateとなっているオレンジのバー)
- 描画(紫のバーから緑のバーの一連の処理)
1から3までは先ほどの例と同じです。4でJSファイルをダウンロードしています。
ところが、CSSよりも先にJSのダウンロードが完了してしまいます。しかし、ブラウザはCSSのダウンロードが終わるまで、何もしていません。いわゆるアイドル状態です。
そして、スタイルシートのダウンロードが終わり、パース処理をしたあとにJSを実行しています。そして初めて描画処理に至るわけです。
図の緑の線を引いたところ(JSのダウンロードが完了してからCSSのパース処理が終わるまで)がいわば、CSSによるJS実行のブロッキング現象です。バッチリ、ブロックされちゃってますね・・・。参考
ファーストビューのレンダリングブロック
このようなことから必要なCSSはもっとも早く呼ぶべきです。特にファイルサイズが大きなものから呼び出していけば、ダウンロードも早く終わるはずです。
ただし、ページスピードインサイトでは、ファーストビューをいかに早く表示しているかをチェックしています。なので、ファーストビューとは関係のないCSSの読み込みがあると、その分JSの実行も遅れ、さらに表示処理も遅れてしまい「ファーストビューとは関係のないCSSは後で読み込むように!」と怒られてしまうのです。
先ほどの巨大なCSSはどうすればいいのでしょうか?
次回はこの問題をどうやって解決するか、見ていきたいと思います。