日本語 Web フォントの読込み速度を改善する - その3

[日本語 Web フォントの読込み速度を改善する - その2](https://qiita.com/megurock/items/bab9bb9ae5880334ec65) の続きです。
前回は [Google Fonts](https://fonts.google.com/) の仕組みに倣い、任意の日本語フォントを [font-ranger](https://www.npmjs.com/package/font-ranger) で 120 個に分けてサブセット化しました。

今回は更なる高速化を求め、Service Worker を使い、前回サブセット化した Web フォントをキャッシュしてみようと思います。

## サンプルコード

:octocat: https://github.com/megurock/webfont-optimization/tree/precache


## Workbox

キャッシュコントロールの要になる [ServiceWorker](https://developer.mozilla.org/ja/docs/Web/API/ServiceWorker_API) 周りは、[Workbox](https://developers.google.com/web/tools/workbox/) で ｻｸｯ と済ませちゃいます。:sunglasses:

```js:sw-tmpl.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.6.1/workbox-sw.js')
```

### キャッシュ戦略

Workbox にはキャッシュ戦略パターンとして、[全部で 5 つのメソッドが用意されています](https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies)。Workbox のサイトに [Google Fonts のサンプル](https://developers.google.com/web/tools/workbox/guides/common-recipes#google_fonts) があったので、`@font-face` を定義した `/css/fonts.css` には、[StaleWhileRevalidate](https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies.StaleWhileRevalidate) を、そしてフォントファイルには [CacheFirst](https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies.CacheFirst) パターンを適用するようにしました。

```js:sw-tmpl.js
// Cache font stylesheet
workbox.routing.registerRoute(
  '/css/fonts.css',
  workbox.strategies.staleWhileRevalidate({
    cacheName: 'font-stylesheets',
  })
)

// Cache font files
workbox.routing.registerRoute(
  /.*\.woff2?$/,
  workbox.strategies.cacheFirst({
    cacheName: 'web-fonts',
  })
)
```

各々のキャッシュ戦略パターンについては、[こちらのページ](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/) に説明があります。

### フォントファイルのプリキャッシュ

全部で 120 個あるフォントファイルをプリキャッシュさせるのが有りなのか、ちょっとよく分からないのですが、**やってみたかったのでやりました:satisfied:**。以下の工程を省略した場合でも、フォントファイルは、リクエストされるタイミングでキャッシュに入るはずですが、今回はブラウザ君にはより能動的に自らプリキャッシュしてもらうことにしました。

### workbox-build を使ったリビジョン管理

**プリキャッシュさせるファイルは、ファイル毎にリビジョンで更新管理を行います。**リビジョン管理を手動でやるのはシンドイですが、Workbox では自動化してくれるツールがあります。今回は、[workbox-build](https://developers.google.com/web/tools/workbox/guides/precache-files/workbox-build) という Node モジュールを使いました。

まず、適当なファイルに、このような記述をしておきます。

```js:sw-tmpl.js
/** 「キャッシュ戦略」からの続きです。**/

// Precache font files (.woff and woff2)
workbox.precaching.precacheAndRoute([]);
```

次に以下のようなプログラムを実行します。

```js:build-wb.js
const workboxBuild = require('workbox-build')

// NOTE: This should be run *AFTER* all your assets are built
const buildSW = () => {
  // This will return a Promise
  return workboxBuild.injectManifest({
    swSrc: './sw-tmpl.js',  // 元ファイルを指定
    swDest: './sw.js',      // ビルドファイルを指定
    globDirectory: './',    // プリキャッシュ対象ディレクトリを指定
    globPatterns: [         // プリキャッシュ対象ファイルを指定
      '**\/*.{woff,woff2}',
    ]
  }).then(({count, size, warnings}) => {
    // Optionally, log any warnings and details.
    warnings.forEach(console.warn)
    console.log(`${count} files will be precached, totaling ${size} bytes.`)
  });
}

buildSW()
```

すると、プリキャッシュ対象のファイルをリビジョンと共に書き出してくれます。（データに変更があればリビジョンも変更してくれるそうです）

```js:sw.js
/** 省略 **/

// Precache font files (.woff and woff2)
workbox.precaching.precacheAndRoute([
{
  "url": "fonts/shirokuma.0.custom-subset.woff2",
  "revision": "57ed11f4f7ecd4c36d1f4e424943cd8d"
},
{
  "url": "fonts/shirokuma.1.custom-subset.woff2",
  "revision": "57ed11f4f7ecd4c36d1f4e424943cd8d"
},
// 以下省略
])
```

## Service Worker の登録

最後にビルドした `sw.js` を HTML に読みこめば完了です！ 

```html:index.html
<script>
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  })
}
</script>