sw-precacheでWEBアプリのオフライン対応をする
service worker
でキャッシュコントロールを行い、オフラインでも閲覧できる状態にできます。
キャッシュコントロールを独自に実装するのは難易度が高く、実装を誤るとF5でのリロードでも更新されなくなってしまいます。
sw-precache
というパッケージを使えば自動でservice worker
のファイルを作成してくれますので、これを使用してオフライン対応を行います。
パッケージのインストール
最初にsw-precache
のパッケージをインストールします。
$ npm install --save-dev sw-precache
実装
sw-precache
の処理を実装します。今回はgulp
でsw-precache
用のタスクを作成しました。
フォルダ構成は以下とします。
- public (公開フォルダ)
- css (
css
ファイルを配置) - img (画像ファイルを配置)
- js (
js
ファイルを配置) - index.html
- css (
- gulpfile.js
- package.json
gulpfile.js
const dest = './public';
gulp.task('sw', () => {
return swPrecache.write(dest + '/service-worker.js', {
stripPrefix: dest,
staticFileGlobs: [
dest + '/css/*',
dest + '/img/*',
dest + '/js/*',
dest + 'index.html'
]
});
});
タスクを実行して作成されたservice-worker.js
をサーバーにアップしてload
イベントで読み込みます。
window.addEventListener('load', function () {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js', { scope: '/' });
}
});
キャッシュするファイルの指定
staticFileGlobs
でキャッシュするファイルを指定します。
service worker
でファイルを指定する場合、例えばhttp://example.com/js/app.js
のファイルを指定するときは/js/app.js
と指定します。
上記タスクではpublic/js/app.js
という形でファイルを指定するため、service worker
で指定されるパスが/public/js/app.js
になってしまいます。
/public
を消したいので、stripPrefix
のオプションでstripPrefix: dest
(stripPrefix: './public'
)と指定しています。
staticFileGlobs
で指定されたファイルは一度キャッシュされるとそれ以降はサーバーのファイルを見に行かず、キャッシュされたファイルを参照するようになります。(F5でリロードしてもキャッシュされたファイルを参照します。)
これによりオフラインの環境でもキャッシュされたファイルを参照し、オフラインでの閲覧が可能になります。
ファイルを更新した場合
service worker
導入後にjs/app.js
を修正したとします。修正したファイルをサーバーにアップしても、service worker
が動作しているため、サーバーのファイルではなく、キャッシュされたファイル(修正前のファイル)が参照されてしまいます。
再度サーバーのファイルを取得して、そのファイル(修正したファイル)をキャッシュしたい場合は、service worker
のファイルで指定しているバージョンを変更する必要があります。
js/app.js
を修正したあとにsw-precache
を実行するとバージョンが変更されたservice worker
のファイルが作成されます。
生成されたservice worker
のファイルをサーバーにアップしてアクセスすると、変更したjs/app.js
だけサーバーから取得し、修正した内容が反映されます。
動的ページの場合
index.html
ではなく、php
などで動的に生成している場合は以下のようにdynamicUrlToDependencies
を指定します。
gulpfile.js
const dest = './public';
gulp.task('sw', () => {
return swPrecache.write(dest + '/service-worker.js', {
stripPrefix: dest,
staticFileGlobs: [
dest + '/css/*',
dest + '/img/*',
dest + '/js/*'
],
dynamicUrlToDependencies: {
'/': [
'./page/app.php',
'./page/header.php',
'./page/footer.php'
]
}
});
});
/
にアクセスした時、レスポンスされた内容がキャッシュされます。
page/app.php
、page/header.php
、page/footer.php
のいづれかが変更されたときに、再度service worker
を更新すると、変更した/
にアクセスした時、サーバーから取得して再びその内容をキャッシュします。
注意点
動的ページのキャッシュは注意が必要です。
例えばphp
でユーザー情報を動的に表示している場合、一度レスポンスされてキャッシュされてしまうと、ユーザー情報を更新して再表示しても内容が反映されません。(再表示してもphp
の処理がサーバーで行われるのではなくキャッシュした情報を表示するだけなので)
実装効率や保守性の観点でindex.html
を複数のファイルに分けたのなら問題ありませんが、ログインユーザーの情報や操作手順によってレスポンスされる内容が動的に変わってしまうような場合はキャッシュが難しいので、動的なページを生成する処理はすべてJavaScript
で行うように修正するなどの対応が必要になります。