以下のコードを見てください。
function loadList() { var ul = document.getElementById('list'); // 【1】 リストの内容を保持するJSON文字列から<li>を作成する関数オブジェクト var rebuild = function(json) { ul.innerHTML = ''; var list = JSON.parse(json); for (i = 0; i < list.length; i++) { li = document.createElement('li'); li.innerHTML = '<a href="#content" onclick="openContent('+ list[i].id + ')">' + escapeHTML(list[i].title) + '</a>'; ul.appendChild(li); } } // 【2】 localStorageからJSONの取得を試みる var json = localStorage['list-ajax']; if (json != null) { // 【3】 localStorageにデータがあればそれを表示 rebuild(json); } else { // 【4】 まだデータがないときは「読み込み中...」を表示 ul.innerHTML = ''; var li = document.createElement('li'); li.innerHTML = navigator.onLine ? '読み込み中...' : 'データを取得できません'; ul.appendChild(li); } // 【5】 最新のデータを非同期で取得 if (navigator.onLine) { var xhr = new XMLHttpRequest(); xhr.open('GET', 'list-ajax.txt?rnd=' + new Date().getTime(), true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { var json = xhr.responseText; // 【6】 次回の呼び出しに備えてlocalStorageにJSONを保存 localStorage['list-ajax'] = json; // 【7】 最新のデータで再表示 rebuild(json); } }; xhr.send(); } }
【1】の部分は、JSON文字列をパースしてJavaScriptの配列を組み立てて、その配列から<li>タグの内容を生成する関数オブジェクトを作成する部分です。localStorageからの表示とAjaxでの表示の2カ所で同じ処理が必要なので関数化しました。
【2】では、すでにlocalStorage内にJSONデータが保存されているときにその値を取得しています。
【3】では、localStorageにすでにデータが存在するときは取得したJSON文字列を使って、【1】の関数を呼び出し画面を描画します。
【4】では、localStorageにデータがなかったときは、ネットワーク経由でのAjax呼び出しが必要なので、時間がかかるため「読み込み中...」メッセージを表示します。
【5】では、最新のデータをネットワーク経由で取得して、【6】で取得したJSON文字列をlocalStorageに保存して、【7】で表示します。
このコードを使うと、localStorageにデータがあれば、それをすぐに表示してネットワークからデータを取得する時間のかかる処理は非同期通信で裏側で実行されるようになります。
このアプリでは一覧画面の他にも、一覧画面をクリックして開く詳細画面もJSONでデータを取得するようにしているため、こちらはプリフェッチに対応してみます。
プリフェッチとは、いわゆる「先読み」の技術で、ここでは、一覧画面を表示しているときに、次にユーザーがクリックしそうなリンクの先のデータを裏側で読み込んでおいてlocalStorageなどに保存し、いざユーザーがタップして開いたときには、すでにロード済みのデータを即座に表示しています。
フロー図で表すと、以下のイメージです。
まずは、詳細画面を表示する部分をlocalStorageに対応します。前回のコードのopenContent()関数を以下の2つの関数で置き換えます。
function prefetchContent(id, cb) { // 【1】 サーバからJSONデータを取得 var xhr = new XMLHttpRequest(); xhr.open('GET', 'content-ajax.txt?id=' + id + '&rnd=' + new Date().getTime(), true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { var json = xhr.responseText; // 【2】 localStorageにJSONを保存 localStorage['content_' + id] = json; // 【3】 保存後のコールバック関数が指定されたときは、それを呼び出す if (typeof(cb) == 'function') { cb(json); } } }; xhr.send(); } function openContent(id) { // 【4】 JSONをパースして詳細画面を組み立てる関数 var refillContent = function(json) { var div = document.getElementById('content'); var content = JSON.parse(json); div.innerHTML = escapeHTML(content.content); } // 【5】 localStorageよりデータを取得 var json = localStorage['content_' + id]; if (json != null) { // 【6】 localStorageにデータがあればその内容で表示 refillContent(json); } else { // 【7】 localStorageにデータがなければサーバから取得して表示 prefetchContent(id, refillContent); } }
一覧のlocalStorage対応と、ほとんど同じです。
prefetchContent()関数は、AjaxでJSONデータを取得して、それをlocalStorageに保存する関数です。openContent()関数は、localStorageを検索してデータがあれば、それをそのまま表示し、なければprefetchContent()関数を呼び出してデータを取得してから表示する関数です。
localStorageのキーには、「content_ID番号」という文字列を採用しました。キーの名前は、このようにID番号だけでなく、プレフィックスを付けたり、データが更新されたときのキャッシュ管理を簡単にするために最終更新時刻をキーに入れたりします。
最後に、loadList()関数で一覧画面を描画する部分にプリフェッチのためのコードを追加して完成です。
function loadList() { var ul = document.getElementById('list'); var rebuild = function(json) { ul.innerHTML = ''; var list = JSON.parse(json); for (i = 0; i < list.length; i++) { li = document.createElement('li'); li.innerHTML = '<a href="#content" onclick="openContent('+ list[i].id + ')">' + escapeHTML(list[i].title) + '</a>'; ul.appendChild(li); // ※以下の3行を追加 if (localStorage['content_' + list[i].id] == null) { prefetchContent(list[i].id); } } } ……【以下省略】
これで、一覧画面を表示するときに、詳細画面のデータがlocalStorageになければ裏側で先に詳細画面のためのJSONを取得しておき、localStorageに保存しておくようになります。
これでひとまずlocalStorageへの対応は完成です。WiFiを使うと、あまり前回との違いが分からないかもしれませんが、グローバルIPでアクセスできるサーバ環境を使える人は、ぜひ3Gネットワークで比較してみてください。
あまりのパフォーマンスの違いにきっとびっくりしますよ。まるでネイティブアプリのように動きます。
次回は、いよいよ本連載の最終回です。キャッシュマニフェストの仕組みを使ってアプリを完全にオフラインで利用できるようにしてみます。それでは、また次回。
Copyright © ITmedia, Inc. All Rights Reserved.