聞いたら一生の宝、プログラミングの基礎の基礎

第7回Webサイトのフロントパフォーマンスの基礎を見直す

みなさんこんにちは、teratail開発チームの出川幾夫です。

ページのパフォーマンスは、あらゆるWebサイトやWebアプリケーションにとってとても重要です。サイト上でのすべての体験に付随し、その良し悪しがUXに直接影響するものだからです。ECサイトなどサイトでの体験がそのまま利益に繋がるようなサービスでは、サイトの読み込み時間で売上が大きく変わることも多いようです。

またSEOにも非常に大きな効果があります。サイトの読み込みにかかる時間が低下することで、Googleが1日にクロールするページ数が増え、新規ページが早くGoogleの検索結果に現れるようになります。

参考:Official Google Webmaster Central Blog: Using site speed in web search ranking
http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html

Google Speed InsightやGTmetrixなど、Webサイトのパフォーマンスをスコアで表示してくれるサービスは多く存在します。これらのツールは何をすべきかを簡単に提案してくれるものの、それらがどういった意味を持っていてどのようにパフォーマンスが改善するのかを意識できていない人も多いのではないでしょうか。

そこで今回は、効果の上がりやすいフロントエンド部分のパフォーマンスチューニング方法について、基礎に立ち返って、その対策にどのような意味があり、どういった効果があるのかを目的別に振り返ってみたいと思います。

体感速度の向上

実際の処理速度は速くなくても、少しの変更でユーザの体感速度を大きく向上させることはできます。一般的にユーザの体感速度の多くはクライアント側での処理が大きな割合を占めます。できるだけ早くページのファーストビューを表示し、ユーザが行動可能な状態にすることが大切です。より具体的には、サーバからのレスポンスが到着してから利用可能になり次第、早くに表示させることになります。

JavaScriptやCSSの読み込みのタイミングを調整するのが最も簡単です。読み込まれてから実行されればいいJavaScriptの読み込みはbodyタグ内の最下部に、ファーストビューを決めるCSSはheadタグ内に入れるとするのが一般的です。JavaScriptを下に書くことでHTMLのレンダリングがブロックされなくなるため、ファーストビューの表示が早くなります。

参考:css、javascriptの記述場所について
https://teratail.com/questions/9325

ファーストビューに影響しない優先度の低いCSSは遅延読み込みさせるのもよいでしょう。サイズの大きな画像が多いページでは、その画像部分だけ遅延読み込みさせるのも効果的です。

またページ内で外部ドメインのファイルを多く読み込んでいる場合は、DNSプリフェッチを設定することで名前解決をページが読み込まれてすぐに行わせることもできます。これによって以降の外部ファイルの読み込み時に名前解決による待ちを省略することができます。

<link rel="dns-prefetch" href="http://www.example.com/" />

また読み込みの進捗を表示することも、ユーザの待つことに対するストレスを軽減させることができます。読み込みに時間がかかるデータの読み込み中にインジケーターを表示させるなどがこれにあたります。

接続の最適化

ブラウザはWebページを1つ読み込む際、HTMLファイルを読み込んだ後に中に記述されているCSSや画像など多くのリソースのリクエストをサーバに飛ばします。Webサーバとの接続を最適化し、この多くのリクエストによる遅延を減少させるのもパフォーマンスに大きな効果があります。

ApacheやNginxなどの主要なWebサーバではKeepAliveという設定項目があります。これを有効化すると一度張ったHTTP接続によるTCPコネクションを再利用できるようになり、オーバーヘッドとなるTCPの3ウェイハンドシェイクの時間を省略することができます。ページ内のリクエスト数が多い場合には特に有効です。Webサーバの最大接続数などに注意しながら有効化を考えてみるとよいでしょう。

参考:HTTP接続のプーリングは可能か?
https://teratail.com/questions/14808

またドメインシェアリングという方法もあります。多くのブラウザでは同一ドメインに対する同時リクエスト数が6に制限されていますが、画像などの静的リソースを別のドメインに設置しそちらを読み込むことで、実質的にブラウザ側の同時リクエスト数を増やすことができます。

jQueryやCSSフレームワークのリクエストも、同ドメインに設置するよりも、それらを提供しているCDNサーバから読み込む方が、並列読み込みができるぶん読み込み完了までの時間が短くなる場合があります。小さい画像等が多い場合などは、画像配信用のサーバを用意するのが効果的です。

リクエスト数の削減

JavaScriptやCSS、画像などのリクエストを削減することも大きな効果があります。上記の通りブラウザの同時接続数には制限があるので、リクエストを減らすことでそれらが空くのを待つ時間を少なくすることができます。不要なファイルをリクエストしないのはもちろん、同じリソースでも工夫次第でそのリクエスト数を減らすことができます。

CSSスプライトは画像のリクエスト数を減らす代表的な例です。小さな画像を1つの画像にまとめてCSSの操作で個別に表示することで、複数の画像のリクエストを1つにすることができます。小さな矢印やアイコンなどは積極的にCSSスプライトにすべきです。またFont AwesomeなどのCSSによるアイコンセットを利用すればCSSで軽量にアイコンを利用でき、画像そのものの使用を減らすこともできます。

またCSSやJavaScriptファイルのリクエストも多くなりがちなリクエストのひとつです。小さなCSSやJavaScriptファイルは別ファイルにせず、HTMLにstyleタグやscriptタグで埋め込むことでリクエスト数を削減できます。

RequireJSやBrowserify、Webpackなどを用いてJavaScriptファイルを細かくモジュール化しつつも、最終的に1つのJavaScriptファイルにまとめて出力することで、大幅にリクエストを減らせます。ページ別にそれぞれ1つのファイルにまとめれば、不要なものを読み込む心配もありません。

通信量の削減

Webサーバからダウンロードするデータの容量を減らすことで、データのダウンロードにかかる時間を短縮することができます。またサーバやネットワークの通信帯域を節約することができるため、帯域が小さいモバイル端末には特に効果的です。

サーバ側でデータをgzip圧縮してデータの転送量を一律に減らすのが簡単です。Content-typeごとに圧縮するものを選択することもできます。圧縮にWebサーバのCPUを利用するので、それとのバランスにだけ気をつければ通信量を大きく減らすことができます。

レスポンスヘッダのExpiresヘッダを適切に設定すると、リソースをブラウザ側にキャッシュさせることができます。ブラウザはリソースごとの最終更新日をリクエストヘッダに含めて送り、サーバ側で更新されていなければ304レスポンスが返り、ブラウザがキャッシュを利用する選択をします。これによりブラウザ内にキャッシュされている画像、CSS、JavaScript等を再利用することができます。

Apacheでブラウザキャッシュを設定する場合は、httpd.confでmod_expires.soを読み込んだ上で、ドキュメントルートの.htaccessに以下のような内容を追記するのが簡単です。こちらもファイル形式ごとにキャッシュを保存する期間を設定することができます。

<ifModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/css "access plus 10 days"
    ExpiresByType text/javascript "access plus 10 days"
    ExpiresByType image/jpg "access plus 10 days"
    ExpiresByType image/png "access plus 10 days"
</ifModule>

画像ファイルは特に容量が大きくなりがちなデータのひとつです。あらかじめ圧縮ツールを用いて不要なデータを省いて、透過が必要ないのであればPNGではなく圧縮率の高いJPGにするなどして容量を減らしておきましょう。大きな画像をブラウザ側で縮小して表示してしまっている場合は、前もって小さい画像を作成してからダウンロードさせるようにしましょう。

HTML、JavaScript、CSSを最小化するには最小化ツールを用いるとよいでしょう。上記のWebpackやSass等を使っても個別にファイルの最小化が実現できます。

HTTP/2

2015年5月に、HTTP/1.1よりもパフォーマンスを大きく向上させたHTTP/2の仕様が、RFC7540として公開されました。

今までのHTTP/1.1では同時接続数が制限されているなどの問題点がありました。現在のWebでは1ページで何十ものリクエストとレスポンスがあることは珍しくないため、多くのサイトのパフォーマンスのボトルネックになっていました。パフォーマンスを追求する場合はCSSスプライトやJSの最小化、フロントエンドの配信に大きく気を使う必要があり、開発の流れや開発効率にまで影響を与えていました。

HTTP/2.0では1つのHTTPのコネクションで複数のストリームを流すことができます。このため同時接続数の制限がなくなり、レスポンスを待つことなくリクエストを送れるようになりました。

またHTTP/1.1ではリクエストヘッダが圧縮できず毎回数百byteの容量を毎回送信する必要がありましたが、HTTP/2ではHPACKという形式でヘッダをバイナリ形式で圧縮できるようになりました。これにより各リクエストの通信量が大きく減少します。

他にもリクエストを待たずにリソースをクライアントに送るサーバプッシュなど、パフォーマンスに関わるさまざまな機能が追加されています。

Apacheではバージョン2.4.17から実験的にですがデフォルトで組み込まれていて、オープンソース版Nginxでも1.9.5から採用されています。

参考:mod_http2 - Apache HTTP Server Version 2.4
https://httpd.apache.org/docs/2.4/mod/mod_http2.html
参考:Module ngx_http_v2_module
http://nginx.org/en/docs/http/ngx_http_v2_module.html

すでにGoogleやTwitterではHTTP/2が利用されています。HTTP2はHTTP/1.1との後方互換性を完全に保っていて、ChromeやFirefoxなどの主要なブラウザの多くはすでに最新版で対応していますので、今後はサーバ側の対応次第で少しずつ広まっていくでしょう。HTTPSが必須なのに注意し、パフォーマンスを求めるのであれば今の段階で試してみるのもよいかもしれません。

最後に

今回はWebサイトのパフォーマンスの改善策について、簡単に体系化して紹介しました。

実際にパフォーマンスを確認するには、ChromeやFirefoxなどのリクエストタイムラインが見られるブラウザで確認するか、WebdriverなどでHAR(HTTP ARchive)形式のデータを取得することで測定すると良いでしょう。リクエストタイムラインの縦のリクエスト数と横の読み込み時間をざっと見て、縦のリクエスト数をはじめに減らすのが王道です。

それぞれの方策がどういう意味を持つかを知って、現状に対する効果を考えながらボトルネックを適切に解消してパフォーマンスの向上を図りましょう。

teratail
https://teratail.com

おすすめ記事

記事・ニュース一覧