スマホからウェブにアクセスするユーザが増え、ウェブサイトの表示速度の高速化がより重要な制作の課題になっています。1ページもののサイトなら、フロントエンド・エンジニアが一人で実装できるかもしれませんが、ある程度の規模のウェブサイトではワークフローやサイト全体の設計にも関わってきます。また、表示速度の高速化の方法を知らなければ、最適化しやすい、より高度なデザインは実現できないでしょう。エンジニアだけでなく、デザイナーやディレクターがこういった情報を知っていれば、よりスムーズに結果を出せるウェブサイト制作ができるはずです。
ページ表示速度の改善にはいろいろな方法がありますが、この記事では一番効果がありそうなところから攻めていきたいと思います。自分もまだまだ勉強中なので、まずはfilament groupのScottさんの記事 やClearleftのJeremyさんの記事 を参考に、フロントエンドでできることにフォーカスして整理してみました。
目次
ユーザの体感速度を改善する
表示パフォーマンスを最適化する際に一番大切なのは、ユーザの体感速度(perceived performance)を向上することです。極端に言えば、ページのサイズを2MBから800KBに圧縮できてもユーザが遅いと感じれば意味がありません。画像を最適化して、CSSやJavaScriptをminifyしてgzipして、外部リソースへのリクエスト数を減らすといった施策ももちろん大切です(というか、基本ですね)。しかし、たとえば、8MBのページでも体感速度を改善できる ことを知るのも重要です。
「Above the Fold」をいち早く表示させる
具体的には、スクロールせずに見える「Above the Fold」のコンテンツをいち早く表示させて、ユーザが感じる表示速度を改善するという方法です。たとえば、全ページが表示されるまでに20秒かかるページAとページBがあり、それらが以下のように表示されるとします。
ページA
10秒間、真っ白でなにも表示されず、その後の10秒で徐々に全ページが表示される
ページB
3秒後にテキストなどのページ要素が表示され始め、残りの17秒で徐々に全ページが表示される
これら2つのページのどちらが早く表示されていると感じるでしょうか?
ページBのほうが、待たされる時間は短く感じますよね?
GWの渋滞でも完全に車が停止して動かなくなるよりも、少しでも流れて進んでいるほうが短く感じますもんね。
初期表示を早くするために注目すべき2つの指標
スクロールせずに見える画面にコンテンツをいち早く表示するよう改善するには、以下の2つの指標に注意を払う必要があります。
- First Byte
ブラウザがページをリクエストした際に、ウェブサーバから最初のByteが返ってくるまでの時間 - Start Render
ブラウザがページのレンダリングを開始し、コンテンツが表示されるまでの時間
これらの数値はWebPageTest.org などのテストツールを使えば確認できますが、1つ目のFirst Byteについては、サーバやSSLの設定の最適化やCDNの利用が必要になります(Scottさんの記事では、Cloudflare とFastly が紹介されていました)。
しかし、2つ目のStart Renderについては、フロントエンドで解決できることがあります。
ちなみに、このブログのトップページをWebPageTest.org を使って、アメリカからNexus 5のChromeで3G回線でテストした結果は以下のとおりです(WebPageTestでは、接続元の場所や回線速度、ブラウザなどを選択できます):
アメリカからの3G回線での接続だと、First Byteまで3秒もかかっていて表示開始までに5秒近く待たされるんですね。
ダウンロードサイズは111KBと大きくないですが、もし、このサイトが北米にターゲットとするユーザを持つ企業サイトだったら、北米に拠点のあるCDNを使ったり、この記事で紹介する方法を使ってStart Renderまでの時間を改善する価値はあるかもしれません。
フロントエンドで解決できる初期表示の高速化
この記事ではGoogle PageSpeed Insights やScottさんの記事 で紹介されている方法を、4つに分けて整理して紹介します。
ちなみに、ブラウザのページのレンダリングの仕組みを知っていると、これから紹介する方法を理解しやすいので、これを機に「ブラウザの仕組み: 最新ウェブブラウザの内部構造 – HTML5 Rocks 」あたりを読んでおくことをお勧めします。
- CSSの読み込みを最適化する
- JavaScriptの読み込みを最適化する
- 初期表示するHTMLを極力小さくする
- リソース全体のサイズを小さくする
a. CSSの読み込みを最適化する
ブラウザがページのレンダリングを始めるには、CSSをすべて読み込んでプロセスする必要があります。そのため、サイズの大きい外部CSSファイルなどは、ダウンロード完了するまでレンダリングをブロックします。これを防ぐには初期表示に使われるCSS(Critical CSSと呼ばれる)を優先して読み込ませ、残りのCSSを初期の描画後に読み込むしかありません。
その際、初期の通信が14.6KB(圧縮後)以上になると次のデータ送信が必要になる ため、できる限り初期表示用のHTML + CSS + JavaScriptをこのサイズに収めるのが最適だそうです。
そんな無茶な感バリバリですが。。。
これを実現する方法がScottさんの記事 とJeremyさんの記事 で紹介されているので、概要を紹介します。
CSS読み込みの最適化のプロセス
- 初期表示に必要なCSS(Critical CSS)をインラインで<style>内に記述する
- フルCSSを初期の描画後に読み込む
- 2回目の訪問のためにCookieを使ったロジックを組み込む
※これに加えてマルチデバイス対応のためのメディアクエリはどうすんの?という疑問もあります。たとえば、モバイル表示の際にはデスクトップ向けのCritical CSSをどうすべきか、とか。。。が、いまの段階ではそっとしておきます。
1. 初期表示に必要なCSS(Critical CSS)をインラインで<style>内に記述する
Critical CSSの抽出にはfilament groupのGruntプラグイン が活躍します。初期表示の画面サイズとページURLを指定すると自動的にCritical CSSを書き出してくれるプラグインです。
抽出したCSSは、<link rel>
で読み込まずにSSIやPHPなどのIncludeを使ってページにインラインで記述します。
<!--#include virtual="/path/to/critical.css"-->
1ページもののランディングページならこれで解決できそうですし、Grunt やGulp を使ったビルドプロセスに組み込んでしまえば、ある程度の規模のウェブサイトでも現実的なワークフローを構築できる気がします。ちなみに、Gulp向けのCritical CSS抽出についてはこのあたり を参考にすると良いでしょうか。
2. すべてのCSSを初期の描画後に読み込む
CSSはHTML内の記述場所に関係なくレンダリングをブロックするので、JavaScriptを使って読み込みます。ここでもfilament groupのloadCSS というスクリプトを使います。このloadCSS を使うと、JavaScript経由で指定したCSSを非同期で読み込むため、初期表示のレンダリングをブロックせずに描画後に非同期でCSSを読み込めます。
ここでは2回目の訪問のことも考え、初期表示用に抽出したCritical CSSも含めたフルCSSを読み込みます。初回訪問の際はCritical CSS部分が重複しますが、ここは初期表示を最優先に考えて目を瞑ります。
3. 2回目の訪問用にCookieを使ったロジックを組み込む
2回目の訪問の際にもインラインCSSが読み込まれると、無駄なリソースを読み込ませることになります。そこで初回訪問の際にCookieを設置します。ユーザのブラウザにCookieが存在する場合はインラインCSSを読み込ませずに、フルCSSファイルを<link rel>で参照してキャッシュから読み込ませます。
最終的なソースコードはEnhance.jsを使ったSSI版 とPHPのテンプレートエンジン「TWIG」を使ったバージョン が参考になります。
手間のかかるプロセスですが、よく考えられた方法ですよね。考案した方々に脱帽です。ほんと関心します。
ちなみに、HTTP/2 だとブラウザがレンダリングの最適化をできるため、こうした回避策は必要なくなるそうです。とはいえ、一夜にして世界のプロトコルがHTTP/2に変わることはありえません。大半の閲覧環境でHTTP/2がサポートされるまでには、まだまだ相当時間がかかりそうです。
b. JavaScriptの読み込みを最適化する
JavaScriptもブラウザのレンダリングをブロックするので、CSSと同じように以下のプロセスで最適化を行います。
- 初期表示に必要なJavaScript(Critical JS)をインラインで記述
- Critical JSでブラウザ環境のテストを行う
- テスト結果により必要なスクリプトを非同期で読み込む
この辺はまだ勉強不足で整理できていないので、参考になりそうなページへのリンクを記載しておきます。そちらをご参照ください。
Enhance.js
JavaScriptの読み込みの最適化についても、filament groupがEnhance.jsというスクリプトを用意しています。より詳しいプロセスについてはREADME.md をご参照ください。また、最後に記載されているサンプルコード が参考になります。YUI Library GET Class でもJavaScriptとCSSを動的に読み込めますね。
その他にも、以下あたりが参考になります。
Remove Render-Blocking JavaScript – Google Developers: PageSpeed Insights
レンダリングをブロックするJavaScriptについての基本がまとめられています。日本語版もありますが、省略されている内容もあるので英語版にリンクしておきます。
Parser Blocking vs. Asynchronous JavaScript – Google Developers
非同期で読み込むJavaScriptについて詳しく説明されています。
- JavaScriptはインラインでも外部ファイルでも、
<script>
タグで読み込まれるものはデフォルトではレンダリングをブロックする <script async>
オプションを使うことで非同期にJSを読み込める
Optimizing Site Speed: Asynchronous and Deferred Javascript – SEO Hacker
非同期とかAsynchronouslyとか言われてもなんのことやら?ですよね?そんな方には、JavaScriptの読み込み方法(normal、defer、async)について、図解(インフォグラフィックス)付きで詳しく説明しているこの記事がオススメです。
- 通常(normal)の読み込みの場合、HTMLのレンダリングを止めてJavaScriptを読み込み、実行する。
- deferの場合、HTMLがすべて読み込まれるのを待ってからJavaScriptが実行される。JSの読み込みはHTMLが読み込まれている際に行われる
- asyncの場合、JavaScriptが読み込まれている際はレンダリングをブロックしないが、実行する際にレンラリングをブロックする
c. 初期表示するHTMLを極力小さくする
前述したように、ブラウザがページをリクエストしてウェブサーバとデータのやりとりをする際、何往復ものデータのやりとりがあるわけですが、最初のリクエストが14.6KB(圧縮後)を超えると次の往復が必要になります。データ送信の往復が多くなると、スマホのように通信に遅延が起こりやすい環境下では、特にページ表示に余分な時間がかってしまいます。そのため、初期表示用のHTML + CSS + JavaScriptのサイズを14.6KB以下に抑えて、残りは後から読み込むことで初期表示の高速化ができます。
これを実現するために、HTMLもCSSやJavaScriptと同様に初期表示に必要な最低限のHTML(Critical HTML)以外は、Ajaxを使って後から読み込む方法が考えられます。
Ajax Include Pattern
ここで再び登場するのが、filament groupのAjax Include Pattern というスクリプトです。このスクリプトを使うと、Ajaxを使って初期表示が完了してから外部HTMLファイルを読み込めます。たとえば、以下のように記述すると<a>
タグ部分をdata-replace
で指定した外部HTMLファイル、path/to/file.html
で置き換えてくれます。
<a href="path/to/file.html" data-replace="path/to/file.html">続きを読む</a>
この書き方なら、万が一JavaScriptがオフでもリンクから外部HTMLにアクセスが可能です。
HTTPリクエストの数は増えますが…
ちなみに、この方法を使うとHTTPリクエストの数は増えますが、ユーザの体感速度に影響を与えなければ良しとします。
要確認事項
Ajaxで読み込んでいるので、たとえば外部HTML内でpicturefill のようなJavaScriptを使いたい場合はどうなるのかとか、この辺は検証が必要です。
d. リソース全体のサイズを小さくする
各ファイルをminifyしたり圧縮したり、画像を最適化したりなど、表示パフォーマンス最適化の基本のキ的なことですね。日本語の情報もたくさんあると思うので、ここでは省略させてもらいます。
まとめ
技術的にはこういった方法が最適なのは理解できますが、手間のかかる作業も多く、運営やメンテナンスのワークフローを考えると現実的なものなのか、なかなか判断が難しいところです。また、表示速度の高速化は重要だというのは理解してますが、費用対効果はどうなのかというのも気になります。表示速度に関連したAmazonやGoogleの事例(PDF資料へのリンク) は有名ですが、これらメガサイトの規模や用途との比較が妥当なものかというと疑問が残ります。
ただ、こういった手法を知っておいて、プロジェクトベースで導入できるように準備しておくことが大切だと思います。いつでも動けるように準備しておけば、必要な時に、よりスピーディに正確な判断ができますよね。
最後に、まだまだ僕も勉強中なので、間違った情報やより良い情報をご存知でしたら、ぜひ、この投稿へのコメントやTwitter 、Facebook などでお知らせください。
2015年5月5日に公開され、2016年1月30日に更新された記事です。
About the author
「明日のウェブ制作に役立つアイディア」をテーマにこのブログを書いています。アメリカの大学を卒業後、ボストン近郊のウェブ制作会社に勤務。帰国後、東京のウェブ制作会社に勤務した後、ウェブ担当者として日英バイリンガルのサイト運営に携わる。詳しくはこちら。
ウェブ制作・ディレクション、ビデオを含むコンテンツ制作のお手伝い、執筆・翻訳のご依頼など、お気軽にご相談ください。いずれも日本語と英語で対応可能です。まずは、Mastodon @rriver@vivaldi.net 、Twitter @rriver 、またはFacebook までご連絡ください。