WebアプリケーションではJavaScriptの読み込み位置をHTMLの最後に行うことで、ページが表示されるまでの体感速度を向上させる手法があります(プログレッシブ・レンダリング)。PhoneGapアプリケーションでも<script>の読み込み位置によってアプリケーションの起動速度に影響が出てくるのでしょうか。いくつかパターンを用意して検証してみます。
JavaScript読み込み位置における起動時間パフォーマンス差
PhoneGapに付属しているターミナル/コマンドプロンプト用のcreateコマンドで作成したプロジェクトでは、Jasmineによるテスト用のコードが付属しています。また、サンプルアプリケーションではJavaScriptの読み込み位置が<head>要素内の末尾から、<body>要素内の末尾に移動しました。
外部JavaScriptファイルの読み込みをHTMLの最後に行うことで、ページのレンダリングを先に行った後に、JavaScriptのロードが行われることになります。PhoneGapアプリケーションではJavaScriptの読み込み位置や処理の順番が、起動時間にどのように影響してくるのでしょうか。いくつかのパターンを用意して検証してみます。
PhoneGapアプリケーションにおける、アプリケーション操作可能になるまでのイベントを把握してみましょう。
- load
- ドキュメントの読み込みがすべて完了した時点で発火(Window)
- DOMContentLoaded
- DOMの構築が完了した時点で発火(Document)
- onDOMContentLoaded
- Cordova内部のイベント。Webページの読み込み・パースが完了した時点で発火
- onNativeReady
- Cordova内部のイベント。Cordovaのネイティブ側の使用準備が整った段階で発火
- onCordovaReady
- Cordova内部のイベント。Cordovaで提供されるJavaScriptオブジェクトがすべて作成された時点で発火
- onCordovaInfoReady
- Cordova内部のイベント。Device APIでデバイス情報を取得できるようになった段階で発火
- onCordovaConnectionReady
- Cordova内部のイベント。Connection APIで回線情報を取得できるようになった段階で発火
- onDeviceReady
- Cordovaの機能が利用できるようになった段階で発火
Cordova内部で利用されているイベントは、CordovaのJavaScriptファイルを修正しない限りは捕捉することができません。今回は、load, DOMContentLoaded, onDeviceReadyの3種類のイベントと、JavaScriptの処理完了までの時間を計測してみました。
- JavaScriptのロード位置を<head>内末尾にした場合
- ループ処理をloadイベント後に行った場合
- ループ処理をDOMContentLoadedイベント後に行った場合
- ループ処理をdevicereadyイベント後に行った場合
- リモートホストのJavaScriptファイルを<script>タグで読み込んだ場合
- リモートホストのJavaScriptファイルをdevicereadyイベント後に読み込んだ場合
- JavaScriptのロード位置を<body>内末尾にした場合
- ループ処理をloadイベント後に行った場合
- ループ処理をDOMContentLoadedイベント後に行った場合
- ループ処理をdevicereadyイベント後に行った場合
- リモートホストのJavaScriptファイルを<script>タグで読み込んだ場合
- リモートホストのJavaScriptファイルをdevicereadyイベント後に読み込んだ場合
なお、計測環境は次のとおりです。
- デバイス: iPad 2
- Cordova: 2.1.0
計測に使用したファイル・計測結果
ベースとなるHTMLファイル/JavaScriptの読み込み/ループのコードは次のとおりです。
リモートホストに配置するJavaScriptファイルサイズは、約2Mとします。
上記の条件をもとにそれぞれ5回試行し、平均値を採用しました。結果は次のとおりです。なお、数値の単位はミリ秒となっています。
表1 JavaScriptの読み込み位置を<head>要素末尾にした場合
実施内容 | load | DOMContentLoaded | deviceready | ループ終了/JSロード完了 |
A. ループ処理をloadイベント後に行った場合 | 44 | 43 | 728 | 595 |
B. ループ処理をDOMContentLoadedイベント後に行った場合 | 597 | 44 | 730 | 596 |
C. ループ処理をdevicereadyイベント後に行った場合 | 53 | 44 | 172 | 724 |
D. リモートホストのJavaScriptファイルを<script>タグで読み込んだ場合 | 1677 | 1678 | 1816 | 1677 |
E. リモートホストのJavaScriptファイルをdevicereadyイベント後に読み込んだ場合 | 43 | 42 | 171 | 1521 |
表2 JavaScriptの読み込み位置を<body>要素末尾にした場合
実施内容 |
load | DOMContentLoaded | deviceready | ループ終了/JSロード完了 |
A'. ループ処理をloadイベント後に行った場合 | 44 | 43 | 729 | 595 |
B'. ループ処理をDOMContentLoadedイベント後に行った場合 | 598 | 44 | 731 | 597 |
C'. ループ処理をdevicereadyイベント後に行った場合 | 44 | 43 | 173 | 725 |
D'. リモートホストのJavaScriptファイルを<script>タグで読み込んだ場合 | 1598 | 1597 | 1732 | 1598 |
E'. リモートホストのJavaScriptファイルをdevicereadyイベント後に読み込んだ場合 | 46 | 45 | 175 | 1658 |
計測結果からわかること
筆者が行った環境では、JavaScriptの読み込み位置(head要素、body要素)におけるパフォーマンス差に明確な違いは見受けられませんでした。
読み込み位置におけるパフォーマンス差を考慮しない場合、上記のパターンでアプリケーションが高速に起動したのは
- ループ処理をdevicereadyイベント後に行った場合
- リモートホストのJavaScriptファイルをdevicereadyイベント後に読み込んだ場合
の2つです。これは、アプリケーションの画面描画がdevicereadyイベント後以降に行われることに由来すると考えられます。
表4 ループ処理、JSファイルの読み込みとdevicereadyイベントの位置による違い
実施内容 | deviceready | ループ終了/JSロード完了 |
A. ループ処理をloadイベント後に行った場合 | 728 | 595 |
B. ループ処理をDOMContentLoadedイベント後に行った場合 | 730 | 596 |
C. ループ処理をdevicereadyイベント後に行った場合 | 172 | 724 |
D. リモートホストのJavaScriptファイルを<script>タグで読み込んだ場合 | 1816 | 1677 |
E. リモートホストのJavaScriptファイルをdevicereadyイベント後に読み込んだ場合 | 171 | 1521 |
これらの結果から、HTMLが単純な場合は<script>タグの位置で動作速度が大幅に変わることはなく、devicereadyイベントの発火前に時間のかかる処理が記述されていないことがポイントになってくることがわかります。
devicereadyイベントが発火する前に、JavaScriptで負荷の大きい処理や大きいサイズのファイルをロードしようとした場合、その間スプラッシュ画像が表示されることになります。結果、起動に時間のかかるアプリケーションができてしまうことになります。デバイスが古いモデルや低速通信環境下にいる場合、より顕著になります。
検証の結果から、PhoneGapを利用したアプリケーションを作成する場合に注意したいことをまとめます。
- devicereadyイベント前に負荷の大きい処理や、リモートホスト上のサイズの大きいファイルをロードさせない。これらはすべて、devicereadyイベント発火後に行わせるように
- 可能な限り、ファイルはすべてローカルに配置する
- リモートホスト上のファイルを使用する場合、リソースの圧縮やWebサーバのgzip圧縮転送を利用して転送量を少なくする
外部WebサービスのAPIなどを利用している場合、<script>タグを使わずに次のようなJavaScriptコードを用いることで、任意のタイミングでJavaScriptファイルを読み込むことができます。
規模の大きいPhoneGapアプリケーションを開発していて起動速度が当初に比べて遅くなってきていると感じた場合、これらの処理の順番を最適化することで改善する可能性があります。一度チェックしてみましょう。