PWA(Progressive Web Apps)

PR

本ページではYouTubeやXなどのWebサイトなどで使われているPWA(Progressive Web Apps)について解説します。

目次

PWA(Progressive Web Apps)とは

 PWAとはWebサイトの中で任意のページをスマートフォンやPCにインストールしてアプリケーション(ソフトウェア)のように使用できる仕組みです。

 ホーム画面などに追加でき、構造によってはオフラインでも動作します。また、PCやスマートフォンで共通してアプリストアを経由せずにインストールでき、Service Workerによるキャッシュ管理により二回目以降のデータの読み込みが早くなるのが特徴です。

 YouTubeやXなどのWebサイトでも対応しており、一部機能をオフラインで利用できるようになっています。

 本サイトでは、音程チェッカー Onsa や モーションキャプチャー などのWebアプリをPWAに対応させています。

chromeでPWAによるインストールを試みたときの画像

音程チェッカー Onsa をPC版Google Chromeでインストールしようとしたときの表示です。ブラウザによって動作は異なりますが、Chrome、Edge、Firefoxなど多くのブラウザがPWAに対応しています。

Service Workerとは

 Service WorkerはWebアプリの裏側(バックグラウンド)で動き、ページを開いていないときでも処理を行える仕組みです。

 これにより、アプリに必要なデータをあらかじめ保存(キャッシュ)しておくことにより、オフラインで機能を使えたり、次回以降の読み込みを高速化できるようになります。

 以下のコードはキャッシュ優先のService Workerです。PWAのスコープ以下のファイルがfetch(ファイルの取得処理)されたとき、すでにあるキャッシュを確認し、キャッシュがあればそれを返します。キャッシュがなければ通常通りネットワークからfetchして、その結果をキャッシュに保存したうえで返します。

ソースコード:sw.js
// 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();
        })()
    );
});

PR