増井 雄一郎

HTML5でサイトを高速化─wri.peで学ぶ、イマドキのWebアプリの作りかた(後編)

前回の記事では、 wri.peの紹介と、HTML5のApplication Cacheを使ったHTMLや画像などの読み込み高速化の話をしました。今回は、Web StorageのlocalStorage/sessionStorageを使い、Ajax通信部分と表示の高速化を行う手法を説明しましょう。

localStorageを用いた通信と表示の高速化

前回のApplication Cacheで、HTMLの読み込みが高速化されました。これでも十分速くはなったのですが、画面に全ての情報が表示されるまでには、ちょっと時間がかかります。

wri.peではメモの一覧や、メモ本体の読み込み・保存はXHR(XMLHttpRequestの略。Ajax通信で使うモジュール名)を使って行っています。この内容は常に変更されるためApplication Cacheに入れるわけには行きません。そのため、実際に画面に全ての情報が表示されるのは、XHRでメモ一覧を取得した後になります。

回線が安定していれば大体一秒程度の時間ですが、ページ遷移ごとに1秒待たされると「さくさく動く」という感じではなくなってしまいます。

そこで、サーバから取得した情報をJavaScriptでキャッシュしておき、次回アクセス時には、このキャッシュの内容を表示してから、XHRを使い最新の情報をサーバから取得するようにします。

これにより、まずはキャッシュの内容を表示し、その後XHRで取得した内容を表示するため、キャッシュが古かった場合には、画面の書き換えが発生します。

一つのブラウザでwri.peを使っていれば、前回の保存内容が最新のモノなので、多くの場合はキャッシュで問題ありません。

JavaScriptでキャッシュを行うには、Web StorageのlocalStorageという機能を使います。

localStorageでは、localStorage.key1 = 'ABC';の様にlocalStorageオブジェクトに設定したデータをページの遷移やリロードに関わらず保存します。ここにXHRで取得した情報を入れておけば、キャッシュとして利用することができます。

localStorageに保存したデータは、プロトコル、ホスト名、ポートが同一の場合に読み出すことができます。そのため、同一ホスト名で動いている別のJavaScriptから情報を読み出せるため、独自ドメインなどで運用しない場合は、気をつけて使用してください。

XHRとlocalStorageを使ったサンプルが下記の様になります。

(invalid jsdo.it code)

効果を分かりやすくするために、通信部分に1秒の遅延を入れてあります。リロードするたびにランダムでJSONを取得しますが、その前に前回取得したJSONを表示する様になっています。

このようにlocalStorageに前回のアクセス情報を保存しておくことで、XHRを待たずにユーザに情報を表示させることができます。

iOSで勝手に再読込がかかってもsessionStorageがあれば大丈夫

iOSでWebアプリを作る上での最大の敵は、別のウインドウや別のアプリに切り替えた後、アプリのウインドウに戻った際に行われるページのリロードにあると思います。

この仕組みのおかげでユーザが入力中の内容が消えてしまい、ユーザビリティが大きく損なわれてしまいます。wri.peでもメモを入力中に他のアプリに切り替えただけで、メモが消えてしまってはメモ帳として使うことができません。

入力中の内容を常にlocalStorageに入れておけば、再読込がされても内容を保持することができます。しかしlocalStorageはドメイン単位で保持されるので、複数のタブでwri.peを開いていた場合、保持内容が上書きされてしまいます。

Web StorageにはlocalStorage以外に、sessionStorageという規格があります。localStorageは同一ドメインで共有された情報でしたが、sessionStorageはウインドーやタブごとに情報が保持されます。

sessionStorageを使ったサンプルが下記の様になります。sessionStorage.draft_bodyという変数はページが再読込されても保持されます。ウインドウやアプリの切り替えに伴う再読込でも、sessionStorageの内容は保持されます。

(invalid jsdo.it code)

wri.peに限らず入力フォームがありスマートフォン対応しているサイトの場合には、このsessionStorageを使う事でユーザビリティを大幅に向上できます。数行のJavaScriptで対応できるのでスマートフォン対応しているサイトぜひ試してみてください。

デザインはdribbbleから

wri.peを公開後、よく聞かれるのはデザインについてです。wri.peはアプリアイコンと英語の査読を除いて一人で作りました。

デザインをイチから考えるのは難しいので、まずは定番のTwitter Bootstrapを使ってとりあえずアプリを作りました。

その上で、dribbbleという世界のデザイナがデザインやスクリーンショットを投稿するSNSサイト上で「webapp」や「mail」などのキーワードで検索して、自分が目指すようなデザインを探します。

そこで、左にナビゲーションバーを出し、大きなアイコンを表示しているサイトがいくつかあり、これを参考にして、下記の様なデザインにしました。

wri.pe デザイン - 1

はじめは上のような感じで作ったのですが、ナビゲーションとアプリの境目がはっきりするように、下のように色を調整してみました。

wri.pe デザイン - 2

これだと画面に特徴がなく印象が弱いので、白黒を基本にして特徴色として自分の好きなオレンジを選びました。カラーコードを決めるのはCOLOURloversを参考にしました。

wri.pe

wri.pe

wri.pe(モバイル)

wri.pe(モバイル)

フリーでアイコンを配布しているサイトは多くあるのですが、サイズの変更が容易なことと、CSSのみで管理が容易な事からFont AwesomeというWeb Fontsを使っています。

Web FontsもCSS3の一機能で、ブラウザで使うフォントを外部から読み込む事ができます。Font Awesomeでは外字領域にいろいろなアイコンを定義して配布しています。

Font Awesomeはベクターなので、サイズを変更しても絵が崩れない、アイコンの色をCSSで指定できるという利点があります。しかし単色のアイコンしかないという欠点もあります。

デザインができなくても、このように既存のツールや情報の組み合わせで、それっぽいサイトを作ることができます。

デスクトップもスマートフォンも一つのHTMLで実現

Twitter Bootstrapでは、“Responsive utility”を使うことで、一つのHTMLで複数のデバイスに対応させる事ができます。wri.peも開発当初はこの機能を使っていたのですが、最近のスマートフォンは解像度が上がりタブレットとの境界が曖昧になっており、単純にタブレットやスマートフォンといった区別では、使いやすいサイトを作るのが困難だと感じました。

そこで、Bootstrapを参考にしつつ、スマートフォンやタブレットでは、縦と横でレイアウトが変わるようなライブラリを自作する事にしました。

仕組みとしては非常に簡単で、起動時にJavaScriptでデバイスの種類を判定して、bodyタグのクラスに追加します。これでHTMLのクラス指定だけで、複数デバイス対応のHTMLを書くことができます。

すこし書きやすくするために、下記の様なCSSも定義します。

この様に定義しておくことで、デスクトップでのみ表示させたい部分を<div class="visible-desktop">...</div>で囲うだけで、スマートフォンやタブレットでは表示されなくなります。

(invalid jsdo.it code)

wri.peでは、このようにHTML内でデバイス毎に、表示・非表示を指定することで一枚のHTMLで複数のデバイスに対応しています。

おわりに

2回に渡って、wri.peでHTML5をどのように活用しているかについて解説させて頂きました。

HTML5はアニメーションや見た目の互換性などビジュアル面が注目されがちですが、Application CacheやWeb Storageなどロジック方面も大幅に強化されおり、プログラマとしても面白い点がたくさんあります。今後HTML5が普及していく上で、この記事が少しでも参考になれば幸いです。

wri.peで学ぶ、イマドキのWebアプリの作りかた(前編)」を読む

'; js_seriesContent.className = "js_seriesContent"; js_seriesContent.innerHTML = js_seriestitle.innerHTML; js_seriesContent.appendChild(js_serieslist_ul); if ( js_parent.lastChild == js_superior ) { js_parent.appendChild(js_seriesContent); } else { js_parent.insertBefore(js_seriesContent, js_superior.nextSibling); } if (js_serieslist_li_length > 5) { document.getElementsByClassName('moveToSeriesTop')[0].style.display = 'block'; document.getElementsByClassName('moveToSeriesTop')[0].href = document.getElementsByClassName('seriesmeta')[0].getElementsByTagName('a')[0].href; } })(this, this.document); // ソーシャルボタンをクリックされたらgaに送信 var elements, i; elements = document.querySelectorAll('.sns-buttons > li > a.facebook-btn-icon-link'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Facebook', 'like', '/masuidrive/807/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.twitter-btn-icon-link'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Twitter', 'tweet', '/masuidrive/807/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.google-plus-btn-icon'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Google+', '+1', '/masuidrive/807/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.hatena-btn-icon'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Hatebu', 'bookmark', '/masuidrive/807/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.pocket-btn-icon'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Pocket', 'bookmark', '/masuidrive/807/'); }, false); }

週間PVランキング

新着記事

Powered byNTT Communications

tag list

アクセシビリティ イベント エンタープライズ デザイン ハイブリッド パフォーマンス ブラウザ プログラミング マークアップ モバイル 海外 高速化 Angular2 AngularJS Chrome Cordova CSS de:code ECMAScript Edge Firefox Google Google I/O 2014 HTML5 Conference 2013 html5j IoT JavaScript Microsoft Node.js Polymer Progressive Web Apps React Safari SkyWay TypeScript UI UX W3C W3C仕様 Webアプリ Web Components WebGL WebRTC WebSocket WebVR