CSS Sprite画像はDataURI画像にすべきか?

最近、スプライト画像はDataURIにすべきですか?という質問が多くて、調べてみました。てか、前のもそんな話題があったような。DataURIってなんぞって方は下記を見てほしい。

CSSファイルがパースされなければレンダリングが始まらないのでCSSファイルの肥大化は絶対に避けなければならない。画像の1KBとCSSファイルの1KBを同じように考えてはいけない。 ― ぼくのかんがえたさいきょうのしーえしゅえしゅ

あ、ホントそうだっけーなーと思いつつ、どこぞの資料見たんだっけなーと探してたらあった。

Optimizing the Critical Rendering Path for Instant Mobile Websites - Velocity SC 2013

このセッションはすごく分かりやすいのでオススメです(該当の箇所は12分位から)。というかIlya Grigorik ++

セッション中の資料には、ご覧のとおり、HTMLがパースされてDOMが完成したところで、画面には何も表示されない。感覚的には、スタイルのついてない『Hello world!』くらい表示されてもいいじゃんか!と思うんだけど、表示されない。

次に、CSSがパースされてCSSOM(CSSのDOMみたいなもの?Style Rulesとかとも言ったりする)が構築されるが、まだ画面は空白のままだ。

DOMとCSSOMがガッチャンコしてRender Treeが構築され、そこにレイアウト情報が加わって初めて描画となるんだね。この辺りの更に詳しい情報はHTML5 Rocksの以下の記事がすばらしいので読んでほしい(すぐ眠たくなるけど)。

ここで重要なのはHTMLと(読み込んでいる)スタイルシート(CSS)が無い限り、描画はされないということだ。つまり、CSSの読み込みに手間取ればCSSレンダリングをブロックするということが考えられる。

レンダリングをブロックするのはJSファイルだけかと思ってたけど、スタイルシート(CSS)も気をつけなければならないということが分かる。

そこで冒頭にも述べたように、CSSファイルを出来る限り軽くし、読み込みを速くすることでレンダリングのブロックを回避するという考えになってくると思うが、実際どんなもんなのよ?と思ったので、テストファイル作ってみた。

30個くらいのアイコン画像をスプライト化して読み込んでいるのと、それをさらにDataURIにしているもの比較だ。それらをWebPagetestにかけてみた。

ビジュアル比較テスト結果

各テスト結果

どうでしょうか?

Fully Loadedがノーマルで1.187sで、DataURIが0.994sで、ビジュアル比較においても、表示完了までの時間がDataURIを使用した方が速い。まぁHTTPリクエストが2つと3つじゃ、リクエストの少ないほうが速いのは当たり前なんですけど、ここではレンダリング過程を見てほしい。

ノーマルは0.7秒あたりから画像なしだけどレンダリングが始まっているのに対して、その時点ではDataURIは真っ白な画面のままだ。Start Renderを見ても、ノーマルが、0.697sに対して、DataURIは0.920sで、普通の画像のほうがレンダリングが速く始まっている。

これは先程の、Ilya Grigorik氏のプレゼン内容を理解していれば当然の現象と理解できるだろう。DataURI化した文字列を含んだCSSは59KBサイズもあるのに対して、画像パスで読み込んだCSSは3KBと軽量だ。この読み込みの差が反映された結果になっている。

比較のテストはシンプルな実装だが、現実問題のサイトはもっと複雑だ。どのようなレンダリング過程になるのかはサイトそれぞれだし、DataURIにして読み込み時間を最小限するというのもひとつの手段かと思う。

ただ、我々は機械ではないので、数値を機械的に判断するのではなく、体感速度という観点からも考えなければならない。ノーマルのテストのようなブログレッシブレンダリングは、ユーザーに即座のフィードバックを返している点で、ユーザーに安心感を与えている。

このような事例として、検索サイトのBingはプログレッシブレンダリングをすることでユーザーの満足度を0.7%向上させた (サイトリニューアル並に上昇したようです)という例もある。

であるので、CSSにレイアウト情報をしっかり記述したり、画像が読み込まれていない時点でのスタイルのケア(background-imageだけでなく似たような色のbackground-colorも指定しておくなど)もしておけば、後は画像が当て込まれていくだけなので体感速度は向上するといったことも考えられるだろう。

セッションでは、AFT(Above-the-fold time)、つまりファーストビューで見える範囲だけのCSSをHTMLにインラインで記述し、残りCSSファイルは遅延読み込みするといったテクニックが披露されているが、実際の運用ベースで使うとなると厳しいだろう。

そもそも的な話で、

+ Base64 encoding incurs transfer size overhead of 1.37 times the original data, with another 814 bytes of header data. Server gzip compression reduces this overhead to around 3% so the penalty is relatively small. + The content must be decoded back into it’s original form by the browser. This operation consumes CPU & battery on mobile devices. + If data URIs are included in HTML or uncached CSS, the content of the data URI will be sent to the browser from the HTTP server with each request. + Regardless of whether the data URI content exists in a cached CSS or HTML file, the browser must decode the image each time a page renders: the decoding cost is paid repeatedly every time a page is viewed.

DataURIの画像は、通常の画像に比べて6倍遅いとかゆう記事もありますし、ファイルサイズ自体も元より増加するし、毎回デコードしなければならなかったり、

Inline images judiciously + Inlining increases parse time + External images don't block the parser + Can defer resource discovery and execution + SPDY server push > image inlining

また、Velocity2012でのnderstanding and Optimizing Web Performance Metricsでも、DataURIは慎重に使用しろと言われてますので、用法用量お守りの上、お使いくださいませ。

個人的な意見としては一回ぐらいしか出てこない画像をDataURIするのであれば、そこのビューに埋め込めばいいし、かと言って何回も出てくるような画像であれば、毎回のデコードコストが気にかかるし、ならスプライトでまとめてキャッシュさせたほうが無難でしょ!と思う。またスプライト画像も今回述べたようにレンダリングのブロックにつながる可能性があるので絶対CSSファイルには入れない、使わない。

参考