米アドビ システムズは、5月6日~8日(米国時間)までの3日間に渡って、同社製品のユーザを集めたカンファレンス「Adobe MAX 2013」を開催した。同社では近年HTML5やCSS3を中心とするWeb標準技術のサポートに力を入れており、今年のAdobe MAXでもこれらの技術やツールに関連したセッションが数多く設けられた。本レポートではその中のひとつ、アドビのテクニカルエバンジェリストChristophe Coenraets氏による「Top 10 Performance Techniques for PhoneGap Appplications」の内容をレポートする。

Adobe SystemsのTechnical Evangelist、Christophe Coenraets氏

PhoneGapは、HTML5やJavaScriptといったWeb標準技術を用いて各種モバイルプラットフォーム向けのネイティブアプリを作成することができるオープンソースのフレームワーク。元々はNitobi Software社によって開発され、2011年にアドビが同社を買収したことに伴ってアドビ製品のひとつになったが、現在も開発はオープンソースで行われている。

Coenraets氏のセッションは、PhoneGapアプリを開発する上で押さえておくべきパフォーマンスに関する10のノウハウを紹介するというもの。PhoneGapの名前が付けられているが、JavaScriptを利用する一般的なWebアプリケーション開発でも通用する内容だった。言われてみれば当たり前に思えるような基礎的なテクニックばかりだが、最低限実践すべきチェックリストとしても使うことができそうだ。

その1、UIをサーバサイドで生成しない

最初に示されたのは、従業員データベースのように、idで情報を管理しているようなシンプルなデータ問い合わせの例。Webアプリなどでよく見るのは、次のようにidをパラメータに渡してサーバサイドのスクリプトを呼び出し、UIとなるHTMLコードを生成して返すというもの。

<a href="employee.php?id=101">Dwight Schrute</a>

しかしこの方法は、ユーザが画面のタップなどのアクションを行なってからサーバ側への問合せの中でUIの生成まで行うため、実際に結果が表示されるまでの待ち時間が長くなるのが問題となる。jQuery Mobile HijaxのようなAjaxライブラリを利用することも改善案のひとつだが、最も良いのは、サーバからは必要データのみを受け取り、UIの生成はクライアント側でJavaScriptを用いて行うことだという。

その2、データが返ってくるまでUIの描画を待たない

UIをクライアント側で生成するとして、次のようなコードはどうだろうか。

// Get data
$.ajax({url: "product/101"}).done(function(product) {
             // displayView(product);
});

この例ではUIの生成はクライアント側のdisplayView関数で行うため、その1で挙げた問題はクリアしている。しかし、Ajaxリクエストからの返答があるまでdisplayViewが呼び出されないため、その間クライアント側の処理が停止してしまうという問題がある。画面をタップしてからUIが変化するまでに間が開くと、たとえそれが数秒であってもユーザを不安にさせてしまう。そこでこのような遅延を回避するために、UIの描画はデータ問合せよりも前に開始するのが良いとのことだ。

リクエストの結果を待ってからUI描画を描画するのでは遅い

タップされてリクエストを発行するまでの間にUI描画を行う

その3、データキャッシュを活用する

続いて紹介されたテクニックは、データのキャッシュをうまく活用すること。一言にキャッシュと言っても、その方法には様々なものがある。セッションでは、次の4つのパターンが紹介された。

1、ローカルストレージや組込みDBを利用した静的なデータのキャッシュ
2、サーバからのレスポンスを蓄積する動的なデータのキャッシュ
3、セレクタのキャッシュ
4、コンパイル済みテンプレートのキャッシュ

3については、同じセレクタの呼び出しを何度も繰り返すのは無駄が多いため、1度作成したセレクタを使い回す方が良いということ。4はページ生成にテンプレートを利用する際のTipsで、嵌め込む値が変わらない間は、一度生成したページを使い回すようにした方が無駄が無いということである。

その4、CSS Transitionsはハードウェアアクセラレーションを意識して利用する

CSS TransitionsはCSS3の新機能では、CSSプロパティが変化する際のアニメーションスピードを設定して自然なUIの遷移を演出することができる。非常に便利な機能ではあるが、普通に使った場合には描画パフォーマンスがあまり良くないため、滑らかな動作が期待できないという問題がある。そこで、CSS Transitionsの利用時にはハードウェアアクセラレーションが効くように意識して実装するのが定石になっているという。具体的には、3D処理のためのtranslate3d関数を併用するテクニックが紹介された。

下図の例は、赤枠が画面で見える範囲を表している。要素の位置を動かすことで赤枠に収まる範囲を変化させ、ページが遷移したように見せる手法である。

CSS Transitionsを高速化するには?

CSS Transitionsを素直に使った場合、この実装は次のようになる。

.page {
    position: absolite; top: 0; left: 0; width: 100%; height: 100%;
}
.page.left {
    left: -100%;
}
.page.center {
    left: 0;
}
.page.right {
    left: 100%;
}
.page.transition {
    transition-duration: .25s;
}

しかし、これを次のようにtranslate3d関数を使った形に修正することで、ハードウェアアクセラレーションが有効になるため滑らかな描画を実現できるとのことだ。

.page {
    position: absolite; top: 0; left: 0; width: 100%; height: 100%;
    transition: translate3d(0, 0, 0);
}
.page.left {
    transition: translate3d(-100%, 0, 0);
}
.page.center {
    transition: translate3d(0, 0, 0);
}
.page.right {
    transition: translate3d(100%, 0, 0);
}
.page.transition {
    transition-duration: .25s;
}

その5、Clickイベントは使用しない

ClickイベントではなくTouchイベントを使うこと

続いて、Clickイベントの遅延を回避するテクニック。スマートフォンのClickイベントは、画面がタッチされてから発生するまでに300msほどの遅延があることが知られている。スマホアプリの場合、この遅延は体感的に無視できるものとは言えない。そこで、これを回避する有効な手段として、Clickイベントの代わりにTouchStartイベントを使って画面のタッチを検出する方法が紹介された。右図はそれぞれのイベントにおける反応速度を比べた例。シンプルだが極めて有効な手段だということが分かる。

ただし、PCのブラウザではTouchStartイベントに対応していないケースもあるという点に注意しなければならないとのこと。PCとモバイル端末の両方で確実に動作させるためには、端末の種類によって使用するイベントを適切に切り分ける必要がある。Fastclickなどのライブラリを利用すれば、より簡単にこの問題を回避できることも紹介された。

その6、CSS Spritesを活用する

アイコン画像のような小さな画像を大量に使用する場合、1枚ずつリクエストを送って取得するのは効率が悪い。そこで、そのようなケースではCSS Spritesを活用するべきとのこと。CSS Spritesは複数の画像を1枚にまとめることで、リクエストの回数を減らしてパフォーマンスを上げる手法。特別に新しい手法では無いが、依然として活用されていないケースをよく目にするという。

CSS Spritesの問題は、使用する画像の座標指定が面倒なことだが、「Adobe Fireworks」などのツールを使えばCSS Sprites用の画像の作成やCSSの生成を簡単に行うことができるため、コーディング負担を小さく抑えることができる

CSS Spritesを利用してキャラクターの動きを表現したデモ

その7、シャドウやグラデーションは最小限にする

CSS3ではシャドウやグラデーションを簡単に表現することができる。しかし現状ではこれらを使用すると描画パフォーマンスが大きく低下するという。したがって、特に必要でない限は使用しないのが望ましいとのことだ。

その8、リフローさせない

HTMLのDOMのレンダリングにおいて、要素の位置や大きさなどの再計算を行うことを「リフロー」と呼ぶ。リフローが頻繁に発生すれば、当然ながらその分だけパフォーマンスは低下する。そこでリフローを減らすために、DOMに対する要素の追加(appendの実行)を減らすことや、DOMのネストを深くし過ぎないといったテクニックが紹介された。

例えば、次のようにappendが複数回発生するコードを考える。

$('body').append('<ul id="list"></ul>');
for (var i = 1; i < 100; i++) {
    $('#list').append('<li>' + i + '</li>');
}

これは次のように修正することで、リフローの回数が抑制されてパフォーマンス低下を防ぐことができるという。

var list = '<ul id="list">';
for (var i = 1; i < 100; i++) {
    list += '<li>' + i + '</li>';
}
list += '</ul>';
$('body').append(list);

そのほか、div要素の数を最小限にすることも有効な手段との話だった。

このセッションのプレゼン資料のソース。PhoneGapで作られているが、div要素の数は3つに抑えたとのこと

その9、フレームワークは必要かどうかをよく検討してから使用する

最近ではアプリケーションの表現力を上げるための様々なフレームワークが登場しているが、安易なフレームワークの利用はパフォーマンスを低下させることにつながりかねない。それよりも、まずは同じことがCSSで実現できないかをチェックするべきとのことだ。

その10、テストする

10番目のテクニックとしてCoenraets氏は、テストすることの重要性を強調した。その際に重要なことは、実際のデバイス、実際のデータを使い、実際の環境でテストすることである。オフィスのLANの中だけでなく、外に出ていろいろな環境で試さなければならない。特に、地下鉄のようにオンライン/オフラインが頻繁に切り替わる環境でどのような挙動を示すのかもチェックしておくことが大切とのことだ。