本ページではYouTubeやXなどのWebサイトなどで使われているPWA(Progressive Web Apps)について解説します。
PWA(Progressive Web Apps)とは
PWAとはWebサイトの中で任意のページをスマートフォンやPCにインストールしてアプリケーション(ソフトウェア)のように使用できる仕組みです。
ホーム画面などに追加でき、構造によってはオフラインでも動作します。また、PCやスマートフォンで共通してアプリストアを経由せずにインストールでき、Service Workerによるキャッシュ管理により二回目以降のデータの読み込みが早くなるのが特徴です。
YouTubeやXなどのWebサイトでも対応しており、一部機能をオフラインで利用できるようになっています。
本サイトでは、音程チェッカー Onsa や モーションキャプチャー などのWebアプリをPWAに対応させています。

音程チェッカー Onsa をPC版Google Chromeでインストールしようとしたときの表示です。ブラウザによって動作は異なりますが、Chrome、Edge、Firefoxなど多くのブラウザがPWAに対応しています。
Service Workerとは
Service WorkerはWebアプリの裏側(バックグラウンド)で動き、ページを開いていないときでも処理を行える仕組みです。
これにより、アプリに必要なデータをあらかじめ保存(キャッシュ)しておくことにより、オフラインで機能を使えたり、次回以降の読み込みを高速化できるようになります。
以下のコードはキャッシュ優先のService Workerです。PWAのスコープ以下のファイルがfetch(ファイルの取得処理)されたとき、すでにあるキャッシュを確認し、キャッシュがあればそれを返します。キャッシュがなければ通常通りネットワークからfetchして、その結果をキャッシュに保存したうえで返します。
// sw.js
// キャッシュ名.
const CACHE_NAME = 'sw.js例: ver0.14';
const appCaches = [
// キャッシュ対象.
];
// PWAのスコープ.
const scope = "https://test.com/webApp/";
// fetch処理の上書き(横取り).
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if(url.href.startsWith(scope)){
// fetchされたscope内のファイルをキャッシュに入れる.
event.respondWith((async () =>{
try{
// キャッシュを開く.
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(event.request,{ignoreSearch: true});
if(cached){
return cached;
}
const res = await fetch(event.request);
await cache.put(event.request, res.clone());
return res;
}catch(e) {
return new Response('error', { status: 500 });
}
})());
}
});
// インストール時に発火.
self.addEventListener('install', (event) => {
event.waitUntil(
(async () => {
// キャッシュ.
const appCache = await caches.open(CACHE_NAME);
// まとめてキャッシュ.
await appCache.addAll(appCaches);
self.skipWaiting();
})()
);
});
// swが更新されたときに発火.
self.addEventListener('activate', (event) => {
event.waitUntil(
(async () =>{
const cacheNames = await caches.keys();
await Promise.all(
cacheNames.map((cache)=>{
if(cache !== CACHE_NAME){
// CACHE_NAME(verなど)が変わったときにすべてのキャッシュを削除.
return caches.delete(cache);
}
})
)
self.clients.claim();
})()
);
});