最近、プログレッシブWebアプリ(Progressive Web Apps、PWA)に興味津々なフロントエンドエンジニア、いなばです。
今回は、オフラインWebをまずは自分で実際に手を動かしてやってみよう! と調べていたところ、sw-precache というモジュールで、リソースのキャッシュ制御がとても簡単にできたのでご紹介したいと思います。
まずは簡単に使ってみる
まずはシンプルな構成で試してみました。 ソースコードはこちらになります。
https://github.com/i78s/sandbox/tree/develop/sw-precache-sample
手っ取り早くデモを確認したい場合は、 下記のコマンドを実行してService Workerに対応しているブラウザ( ここで確認できます )で実際の動作を見ることができます。
$ git clone https://github.com/i78s/sandbox/tree/develop/sw-precache-sample
$ cd sw-precache-sample
$ npm i
$ npm run start
Service Workerの制約(HTTPS で提供されているページでのみ動作する)があるので、今回のデモはlocalhostで動作するよう、ローカルサーバーが立ち上がるようにしてあります。
こういう画像が表示できたらデモの起動は完了です。
sw-precacheコマンドに渡すオプションの設定
デモでは、まずはシンプルに
- 生成するService Workerのファイルの出力先とファイル名を指定 (
swFile
) - キャッシュしたい静的リソースの指定 (
staticFileGlobs
)
のみにしました。
stripPrefix はビルド時と実行時(今回はappディレクトリがルートになる)で相対パスが不一致になるケースで指定します。 今回は、実行時に app
部分を、ローカルファイルのパスの先頭から削除するようにしています。
sw-precache-config.js
const packageJson = require('./package.json');
module.exports = {
swFile: 'app/service-worker.js',
staticFileGlobs: [
'app/**/*.html',
'app/assets/**/*.css',
'app/assets/**/*.js',
'app/assets/images/**/*'
],
stripPrefix: 'app/',
cacheId: packageJson.name,
verbose: true
};
生成されたservice-worker.jsの中身を見てみると、キャッシュしたいファイルとハッシュがセットになっています。
sw-precacheは、ビルド時にファイルの変更を検知すると自動キャッシュするファイルの追加や削除、ファイルのハッシュ値の書き換えなどを行ってくれるようです。
var precacheConfig = [["assets/css/style.css","ba895704d943226e7dcf1aca60fe001a"],["assets/js/app.js","11d374aaa57d884aff6077e0be15f2f1"],["assets/js/service-worker-registration.js","d60f01dc1393cbaaf4f7435339074d5e"],["index.html","d02fef0bd75bd634e313a953a6e8913f"]];
Service Workerの登録
最後に、生成したservice-worker.jsの登録をするためのJSを読み込ませます。
サンプルでService Workerの登録をするスクリプト( service-worker-registration.js )が用意されているので、これを使うのが一番簡単です。
<script src="assets/js/service-worker-registration.js"></script>
リソースの動的キャッシュ
先ほどのデモではhtmlファイル自体もキャッシュしているので、ローカルサーバーを停止したあともアクセスすることが可能になっています(ぜひ実際にリポジトリをクローンして試してみてください)。
ですが、先ほどの設定(staticFileGlobs)ではローカルにある静的リソースのキャッシュの指定しかできないため、外部にある画像やフォントなどのキャッシュができません。
sw-precache-config.jsには初回アクセス時にキャッシュしたいドメインを設定できる項目 ( runtimeCaching ) があるので、ここに画像とフォントのドメインを追加します。
module.exports = {
swFile: 'app/service-worker.js',
staticFileGlobs: [
'app/**/*.html',
'app/assets/**/*.css',
'app/assets/**/*.js',
'app/assets/images/**/*'
],
stripPrefix: 'app/',
/* 追加ここから */
runtimeCaching: [{
urlPattern: /^https://liginc.co.jp/,
handler: 'cacheFirst'
}, {
urlPattern: /^https://fonts.googleapis.com//,
handler: 'cacheFirst'
}, {
urlPattern: /^https://fonts.gstatic.com//,
handler: 'cacheFirst'
}],
/* 追加ここまで */
cacheId: packageJson.name,
verbose: true
};
これで外部のリソースもキャッシュできるようになったので、デモは完全にオフラインページ対応になりました。
2回目のアクセスから、外部にある画像もフォントもキャッシュされていることが確認できます。 数10msで返ってくるので劇的な速度アップが見込めますね。
静的リソースだけでなく、ajaxのレスポンスもキャッシュすることが可能です(sw-precacheが生成するJSではGETメソッドのときしかキャッシュ処理を行わないようになっています)。
▼WordPress Rest API のレスポンスをキャッシュする例
runtimeCaching: [{
urlPattern: //wp-json/,
handler: 'fastest'
}]
キャッシュする期限や最大件数、キャッシュの使用方法を指定するオプションも用意されているので、かなり細やかな制御を行うことができるようです。
- キャッシュする期限や最大件数などのオプション
- https://github.com/GoogleChromeLabs/sw-toolbox/blob/master/docs/api.md#options
- キャッシュの使用方法を指定するオプション
- https://github.com/GoogleChromeLabs/sw-toolbox/blob/master/docs/api.md#handlers
おわりに
すべてのブラウザでService Workerが使えるようになるにはもう少し時間がかかりそうですが、実際に手を動かしてオフラインで動作するページを作ってみると、なかなか感慨深いものがありました。
何度もアクセスされることを想定したWebサイトにおいてリソース(HTMLやJavascriptファイルなど)のロード時間が圧倒的に短縮できる効果はとても大きいので、隙を見てLIGブログや受託の案件などに積極的に取り入れていこうと思います。
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。