Webパフォーマンス入門: JavaScriptの最適化
高速にJavaScriptを実行するための基本
2017年11月27日
著者: Kameerath Kareem
翻訳: 小川 純平
この記事は米Catchpoint Systems社のブログ記事 Web Performance 101: Optimizing Javascriptの翻訳です。
Spelldataは、Catchpointの日本代理店です。
この記事は、Catchpointの許可を得て、翻訳しています。
JavaScriptは、Webアプリケーションを再定義しました。滑らかなレスポンシブデザインの、動的なWebサイトの時代の到来を告げています。
それは開発者の間で多くの支持を集め、jQueryやAngularJSのような、人気のあるライブラリーやフレームワークは全てJavaScriptで構築されています。
(訳注: Angularの現行バージョンは、正確には主にTypeScriptで構築されています。)
JavaScriptが、現状存在する様々なライブラリーを用いて提供する、無限のデザインの可能性は、Webアプリケーションの開発において欠くことのできないものとなりました。
何故Javascriptを使うのか
今日では、ほとんど全てのWebサイトがJavaScriptを使用し、インタラクティブなデジタル体験を提供しています。
JavaScriptによって、ユーザがボタンをクリックしたりフォームを送信した際に、ページの一部のみをリフレッシュ・読込することができるようになり、毎回ページ全体を読み込まなくても済むようになりました。
スクリプトはDOM要素を操作することができますので、開発者はより柔軟なアプリケーションのデザイン・コーディングができるようになります。
これにより、簡単なJavaScriptを書くだけで、簡単にWebアプリケーションに統合できるウィジェットやプラグインの開発も盛んになりました。
ベストプラクティス
より多くのアプリケーションがJavaScriptで実装されるようになるに連れ、スクリプト言語の利点の代償として、パフォーマンスが犠牲となることが明らかになってきました。
スクリプトはデジタル体験に対し、明らかに悪影響を与える場合があります。
スクリプトのダウンロードや実行に時間がかかりすぎればWebページの読込速度は遅くなります。
同様に、スクリプトでエラーが発生したり、正しくコードが書かれていなければ、ページは全く描画されません。
ですから、JavaScriptをWebサイトで使いたい場合、Webサイト上でシームレスに動くようにすることが重要です。
JavaScriptをWebサイトで利用する際に従うべき、ベストプラクティスをいくつか見ていきましょう。
1. JSファイルをminifyし、単一ファイルに結合する
JSファイルをminifyすることで、余計な文字列(空白やタブなど)やコメントがスクリプトから削除されます。
これはファイルを圧縮し、ファイルサイズを減らすことの一助となります。
Minifyされることでファイルは軽くなっていますので、比較的ダウンロードが速くなります。
GoogleのClosure CompilerやJsMin、UglifyJSなど、ネット上にはいくつか、JavaScriptをminifyするツールがあります。
複数のJSファイルを結合し、ファイルが単一の接続でダウンロードできるようにし、サーバのオーバヘッドを減らすこともお薦めです。
ブラウザは50KBのファイルを1つダウンロードする方が、5KBのファイルを10個、複数の接続でダウンロードするよりも高速です。
Spelldatの見解
弊社の見解としては、上述のminify、JavaScriptファイルの結合は、パフォーマンス上の観点からは、統計的有意な効果はありません。
HTTP/1.1においてKeep-Aliveが有効である、もしくはHTTP/2を使用していれば、同一ドメインからダウンロードされるファイルにおける、コネクションを複数張るオーバヘッドは無視できます。
多数の接続になるデメリットは、TCP 3 Way Handshakesと、TCP Slow Startの影響によるものです。
詳しくは竹洞のブログ記事「画像を分割もしくは合体させてパフォーマンスは本当に変化するのか?」をご覧下さい。
また、昨今では、欧米の企業では、表示開始前、表示完了前、表示完了後と、表示のプロセスに応じて必要なJavaScriptに分けています。
それらを、後述するasync、deferなどのオプションをつけたり、明示的な遅延読込をさせるなどしています。
2. スクリプトの重複とインラインスクリプトを避ける
JSコードをページのHTMLに加えることで、ページのトータルサイズを増加させ、またCSSや(訳注:別ファイルに分離された)スクリプトファイル、画像ファイルとは異なり、HTMLはそれほど頻繁にブラウザにキャッシュされません。
つまり、インラインスクリプトは、ページの読み込みの度、毎回ダウンロードされパースされます。
ですから、JSコードは外部ファイルに分けることがお薦めで、これによりブラウザはファイルをキャッシュし、必要なスクリプトのダウンロードと実行にかかる時間を減らすことができるようになります。
多くのJSライブラリやフレームワークがありますが、これらは定期的にアップデートされ、同一サイト内で複数のバージョンが使われることになる場合があります。
開発者は同じJavaScriptの複数のバージョンを組み込んだり、同じスクリプトを複数回追加してしまったりするかもしれず、これはページ上におけるエラーを誘発したり、正しいページのレンダリングを妨げたりすることまでもあります。
これに加え、<script>タグの重複により、ページにオーバヘッドが発生します。
jQueryやその他のJavaScriptライブラリ・モジュールを使用する際は、最新のバージョンを用い、1つのページに重複して<script>タグを書かないようにすることが重要です。
これにより、コード実行時に、エラーやコンフリクトが発生することがなくなります。
Spelldataの見解
このあたりは、Google PageSpeed Insightsの診断アドバイスと異なるので、疑問に思う方も多いでしょう。
Webサイトの閲覧は、1ページだけを見て終わるなら、インラインにしても良いかもしれませんが、大抵は複数ページに亘って遷移します。
インラインで記述することは、Webページの部分最適化であって、Webサイトを遷移する際の全体最適化とはならない点に注意しましょう。
3. イベントハンドラ
JSでは、カスタムハンドラを追加したり、ページの要素を操作して、よりレスポンシブでインタラクティブなWebアプリケーションを作ることができます。
しかし、あまりに多くのイベントハンドラが発火すると、パフォーマンスが悪化することになります。
イベントハンドラがブラウザのリソースを食い潰さないようにするには、発動するイベントハンドラの数に注意し続けることと併せて、使われていない時にはイベントハンドラを開放する必要があります。
4. DOM操作を減らす
DOM操作はWebアプリケーションをレスポンシブにするために使われますが、これによりページ内のHTML要素の再レンダリングが行われることになります。
このプロセスはリフローと呼ばれます。
リフローの時間はDOM操作の度に発生し、デジタル体験に対する影響を及ぼします。
DOM要素の変更のためにJavaScriptを用いる必要がある場合、コーディングの際には、DOM構造が簡潔で効率的になるように考えてみて下さい。
不要な<div>や<span>は外し、DOMが「深く」ならないようにして、移動するノードレベルが少なくなるようにしましょう。
スタイルの変更やアニメーションは、JavaScriptではなくCSSを用いましょう。
これにより、DOM要素とのインタラクションをかなり少なくできます。
5. asyncとdeferを用いる
ページ内に複数のスクリプトがある場合、並列的なダウンロードはスクリプトによって中断され、スクリプトは1つ1つ、シーケンシャルにダウンロードされていきます。
すなわち、全てのスクリプトがダウンロード・実行されるまで、ページがレンダリングされないということです。
レンダリングの中断を防ぐために、いくつかのテクニックがあります。
- async
-
スクリプトを非同期に読み込むことで、スクリプトがバックグラウンドでダウンロードされている間に、WebページやWebアプリケーションの他の部分に関する処理を行うことができます。
スクリプトのダウンロードが完了した時に、スクリプトは実行されます。 - defer
- deferを用いることで、ページ読み込みが完了した後でスクリプトの読み込み・実行が行われるようになり、これによってスクリプトがレンダリングを中断しないことが保証されます。
asyncとdeferについては、Qiitaで記事を書いたので、そちらも併せてご覧下さい。
<script>タグにasync/deferを付けた場合のタイミング
今後の見通し
JavaScriptは、今や強力な言語に進化しました。
実装が楽になるようになっているため、非常に広く使われるスクリプト言語となっています。
Webサイトからモバイルアプリ、IoTに至るまで、JavaScriptは開発者に好まれる言語であり続けています。