Progressive web apps を実装しました。サイトをインストールしたり、オフラインで動かしたりできるようになりました。

更新 公開)

# [Progressive web apps](https://developer.mozilla.org/docs/Web/Progressive_web_apps) を実装した

```html
<link rel="manifest" href="path/to/app.webmanifest">
<script>navigator.serviceWorker?.register("/service-worker.js").then(_onFulfilled).catch(_onRejected)</script>
```

[Lighthouse](https://github.com/GoogleChrome/lighthouse) で検証できます。

## [Service Worker][ServiceWorker]
[ServiceWorker]: https://developer.mozilla.org/docs/Web/API/Service_Worker_API/Using_Service_Workers

ネットワークリクエストやキャッシュの制御ができます。サイトのオフライン動作を助けてくれます。
```js
// service-worker.js
self.addEventListener("install", _oninstall);
self.addEventListener("activate", _onactivate);
self.addEventListener("fetch", _onfetch);
```

Service Worker ファイルの更新はブラウザによって検出されるようです。

### [`install`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/install_event) イベント
Service Worker がインストールされるときに発行されます。

```js
self.addEventListener("install", event => event.waitUntil(Promise.all([_addResourcesToCache(), self.skipWaiting()])));
```

#### [`ServiceWorkerGlobalScope.skipWaiting`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting)
更新を即座に有効にすることができます。

### [`activate`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/activate_event) イベント
Service Worker が有効化されるときに発行されます。

```js
self.addEventListener("activate", event => event.waitUntil(Promise.all([
    !self.registration.navigationPreload ? Promise.resolve() : self.registration.navigationPreload.enable(),
    _deleteOldCaches(),
    self.clients.claim(),
])));
```

#### [`NavigationPreloadManager.enable`](https://developer.mozilla.org/docs/Web/API/NavigationPreloadManager/enable)
[ナビゲーション先読みを有効にすることができます。](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#service_worker_navigation_preload)

#### [`Clients.claim`](https://developer.mozilla.org/docs/Web/API/Clients/claim)
登録された Service Worker を、再読み込みせずに使用できるようになります。

### [`fetch`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/fetch_event) イベント
サイトのリソースが読み込まれるときに発行されます。

```js
self.addEventListener("fetch", event => _cacheFirst());
```

## キャッシュ
[.htaccess ファイルなどで制御して](https://httpd.apache.org/docs/current/mod/mod_headers.html#header)おくと、[Service Worker][ServiceWorker] がキャッシュを更新するときに、新しいリソースだけをサーバーから取得するようにできます。

```htaccess
<ifModule mod_headers.c>
    Header set Cache-Control "max-age=0"
</ifModule>
```

### [`max-age`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives)
キャッシュの寿命が指定です。

## [Web app manifests](https://developer.mozilla.org/docs/Web/Manifest)

サイトがインストールできるようになります。

[.webmanifest 拡張子](https://web.dev/add-manifest/#create)の [JSON](https://datatracker.ietf.org/doc/html/rfc8259) ファイルです。ブラウザは .json 拡張子にも対応してくれるようです。

```json
{
    "short_name": "アプリの名前",
    "name": "アプリの名前",
    "icons": [{
        "src": "path/to/icon.svg",
        "type": "image/svg+xml",
        "purpose": "monochrome",
        "sizes": "any"
    }],
    "description": "説明"
}
```

[ウェブブラウザの開発者ツールで検証できます。](https://web.dev/add-manifest/#test-manifest)

### 必須

#### [`short_name`](https://developer.mozilla.org/docs/Web/Manifest/short_name) 又は [`name`](https://developer.mozilla.org/docs/Web/Manifest/name)
アプリの名前です。

#### [`icons`](https://developer.mozilla.org/docs/Web/Manifest/icons)

アイコンです。画像オブジェクトの配列で、各オブジェクトの `purpose` でアイコンの用途が指定できます。`monochrome`、[`maskable`](https://web.dev/maskable-icon)、`any` などです。

[192×192 と 512×512 のアイコンを用意するとよいようです。一方で SVG に対応するブラウザもあります。](https://web.dev/add-manifest/#icons)

### [`start_url`](https://developer.mozilla.org/docs/Web/Manifest/start_url)

URL です。
```json
"start_url": "/"
```

ここではトップページを指定しています。

### [`background_color`](https://developer.mozilla.org/docs/Web/Manifest/background_color)

[スプラッシュ画面](https://developer.mozilla.org/en-US/docs/Web/Manifest#splash_screens)の背景色です。
```json
"background_color": "black"
```

ページの [`background-color`](https://developer.mozilla.org/docs/Web/CSS/background-color) と一致しているとよいようです。

### [`display`](https://developer.mozilla.org/docs/Web/Manifest/display)

ブラウザ UI の表示です。
```json
"display": "standalone"
```

ここでは単独アプリのように表示しようとしています。

対応していない場合は、ブラウザが特定の順番で代わりの方法を決定するようです。[`display_override`](https://developer.mozilla.org/docs/Web/Manifest/display_override) でその順番が変更できます。

### [`scope`](https://developer.mozilla.org/docs/Web/Manifest/scope)

ナビゲーションスコープです。
```json
"scope": "/"
```

ここではトップページを指定しています。

### [`theme_color`](https://developer.mozilla.org/docs/Web/Manifest/theme_color)

テーマカラーです。
```json
"theme_color": "yellow"
```

[HTML の`<meta name="theme-color">` タグでも指定できます。`media` 属性と合わせてシステムのテーマに対応することもできます。](https://web.dev/add-manifest/#theme-color)
```html
<meta name="theme-color" media="(prefers-color-scheme: light)" content="yellow">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="lightyellow">
```

ここでは、いわゆるライトモードのときに `yellow`、ダークモードのときに `lightyellow` をテーマカラーとするよう指定しています。

### [`lang`](https://developer.mozilla.org/docs/Web/Manifest/lang)

[言語タグ](https://www.ietf.org/rfc/bcp/bcp47.txt) です。
```json
"lang": "ja-JP"
```

ここでは日本の日本語を指定しています。

### [`orientation`](https://developer.mozilla.org/docs/Web/Manifest/orientation)

画面の向きです。
```json
"orientation": "any"
```

ここでは任意の向きを指定しています。

無効なフォームです。ページの再読み込み、又は外部サービスをお試しください。