HugoサイトのPWA対応
1. Hugoで構築したサイトをPWA化
1-1. PWAとは
- PWAとは、「Progressive Web Apps」の略称で、モバイル向けWebサイトをスマートフォン向けアプリのように使えるようにする仕組みです。 PWAはそれ自体が何か特殊な一つの技術、というわけではありません。レスポンシブデザイン、HTTPS化など、Googleが定める要素を備えたWebサイトであり、オフラインやプッシュ通知に対応するためのブラウザAPI(Service Workerなど)を利用しているWebサイトをPWAと呼びます。
- Web アプリ マニフェストを追加する方法
- 本サイトでは、トップページのみPWA化となります。
1-2. 手順
-
manifest.webmanifestの作成
-
icon画像の作成
-
icon画像の配置
-
manifest.webmanifestの修正と配置
-
ブラウザでmanifest.webmanifestの内容が表示されていることを確認
-
Service Workeの作成
-
Service Workeの配置と確認
1-3. manifest.webmanifestの作成
- Simi Cartのサイトは、PWA マニフェスト ジェネレーターとして「Web アプリ マニフェスト」と、PWA 用にサイズが最適化されたアイコンを自動的に生成してくれます。
-
display:standaloneとしました。 Web アプリを開いて、スタンドアロン アプリのようなルック アンド フィールにします。アプリは、ブラウザーとは別の独自のウィンドウで実行され、URL バーなどの標準のブラウザー UI 要素を非表示にします。
-
scope:ブラウザーがアプリ内にあると見なす URL のセットを定義し、ユーザーがいつアプリを離れたかを判断するために使用されます。scopeは、Web アプリのすべてのエントリ ポイントと出口ポイントを包含する URL 構造を制御します。scope,scope,start_url,scope内に存在している必要があります。
-
scope が相対 URL である場合、ベース URL はマニフェストの URL になります。
-
スコープは、ナビゲーションを現在のサイトに制限します。
-
"scope": "https://example.com/"
- ナビゲーションを現在のサイトのサブディレクトリーに制限します。
"scope": "https://example.com/subdirectory/"
- (注意)scope:ユーザーがアプリ内で の外部に移動するリンクをクリックするscopeと、リンクが開き、既存の PWA ウィンドウ内でレンダリングされます。
- target="_blank"リンクをブラウザー タブで開く場合は、タグに追加する必要があります。Android では、リンクはChrome カスタム タブ**target="_blank"**で開きます。
{
"theme_color": "#a4115e",
"background_color": "#f2f4f4",
"display": "standalone",
"scope": "https://raizin.net/",
"start_url": "https://raizin.net/",
"name": "RAIZIN INC.",
"short_name": "RAIZIN",
"description": "\u6587\u66f8\u7ba1\u7406\u30b7\u30b9\u30c6\u30e0\uff08DMS\uff09Document Management System",
"icons": [
{
"src": "/images/manifest/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/images/manifest/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/images/manifest/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/images/manifest/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
- manifest.webmanifestは、/static/manifest.webmanifestの直下に配置しました。
- chromeのデベロッパーツールで確認します。
1-4. Service Worker
- Service Workerは、Web App Manifestと同じように読み込むのではなく、JSでService Workerを登録する処理を書きます。
- Service Workerを登録する処理を書きます。(ここでは、「Simi Cart」で自動で作成された「generate-sw.js」を使います。)
// Incrementing OFFLINE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
const OFFLINE_VERSION = 1;
const CACHE_NAME = 'offline';
// Customize this with a different URL if needed.
const OFFLINE_URL = 'offline.html';
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
const cache = await caches.open(CACHE_NAME);
// Setting {cache: 'reload'} in the new request will ensure that the response
// isn't fulfilled from the HTTP cache; i.e., it will be from the network.
await cache.add(new Request(OFFLINE_URL, {cache: 'reload'}));
})());
});
self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
// Enable navigation preload if it's supported.
// See https://developers.google.com/web/updates/2017/02/navigation-preload
if ('navigationPreload' in self.registration) {
await self.registration.navigationPreload.enable();
}
})());
// Tell the active service worker to take control of the page immediately.
self.clients.claim();
});
self.addEventListener('fetch', (event) => {
// We only want to call event.respondWith() if this is a navigation request
// for an HTML page.
if (event.request.mode === 'navigate') {
event.respondWith((async () => {
try {
// First, try to use the navigation preload response if it's supported.
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// catch is only triggered if an exception is thrown, which is likely
// due to a network error.
// If fetch() returns a valid HTTP response with a response code in
// the 4xx or 5xx range, the catch() will NOT be called.
console.log('Fetch failed; returning offline page instead.', error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse;
}
})());
}
// If our if() condition is false, then this fetch handler won't intercept the
// request. If there are any other fetch handlers registered, they will get a
// chance to call event.respondWith(). If no fetch handlers call
// event.respondWith(), the request will be handled by the browser as if there
// were no service worker involvement.
});
- script要素の中で、navigator.serviceWorker.registerでService Workerの処理が書かれたJSファイル(上記の例ではgenerate-sw.js)を登録します。
- ここでは、index.htmlのhead内に書いています。
<script>
navigator.serviceWorker.register('/generate-sw.js');
</script>
- chromeのデベロッパーツールで確認します。
2. 本番環境でデプロイ
- manifest.webmanifest内の「http://localhost:1313/」を「https://raizin.net/」に書き換えます。
(参考サイト)
以上