kqito's 技術ブログ

技術やプログラミングについて思ったことを呟くブログ

workboxで簡単にprecacheする

はじめに

今回はworkboxを利用したprecacheをフレームワークworkbox-webpack-pluginなどを利用せずに行いたいと思います。

www.npmjs.com

目標は「シンプルに」実装していきます。

環境

Server

今回はgo言語のwebフレームワークであるginを利用します。

github.com

goには標準ライブラリとしてnet/httpなどありますが、ginによる恩恵が大きいのでこちらを利用。

DBなども用いないので単純なhtmlなどを返すサーバーになります。

Client

こちらは純粋なhtml + jsのみで作成します。precacheする内容は

  • /index.html
  • *.js

とします。

ディレクトリ構成

.
├── build
│   └── main
├── go.mod
├── go.sum
├── main.go
├── refresh.yml
├── static
│   ├── image
│   │   └── react.png
│   └── js
│       ├── service-worker.js
│       └── workbox.js
└── view
    └── index.html

コード

Server

サーバーサイドはmain.goだけになります。

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "path/filepath"

    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    router.GET("/", func(ctx *gin.Context) {
        ctx.File("./view/index.html")
    })

    router.GET("/:file", func(ctx *gin.Context) {
        file := ctx.Param("file")
        fp, err := filepath.Glob("./static/**/" + file)
        if err != nil || len(fp) == 0 {
            ctx.Status(http.StatusNotFound)
            return
        }

        ctx.File(fp[0])
    })

    host := os.Getenv("HOST")
    if host == "" {
        host = "localhost"
    }

    port := os.Getenv("PORT")
    if port == "" {
        port = "8081"
    }

    baseUrl := fmt.Sprintf("%s:%s", host, port)
    log.Println("Listening on", baseUrl)
    log.Fatal(router.Run(baseUrl))
}

service-workerにはスコープの概念があり、/service-worker.jsとリクエストできるように/:fileでは`filepath.Globを用いてstaticファイルを返すようにしています。

が、ここはもっと綺麗に書ると思います。直してないけど

Client

まずはindex.htmlから。

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Service-worker</title>
    <meta charset="UTF-8">
    <script type="module">
      import { unregister, register } from "/service-worker.js"
      register()
    </script>
  </head>
  <body>
    <img src="/react.png" alt="react icon" style="width: 400px; height: 300px;"/>
  </body>
<html>

こちらは

  • /react.pngを表示
  • /service-worker.jsのregister関数を実行する

上記2点を行っているシンプルなhtmlです。

それではその/service-worker.jsが以下です。

const swKey = "serviceWorker";

function registerServiceWorker(sw) {
  navigator.serviceWorker
    .register(sw)
    .then(() => {
      console.log("Register service worker");
    })
    .catch((err) => {
      console.error(err);
    });
}
export function unregister() {
  if (!swKey in navigator) {
    return;
  }

  navigator.serviceWorker.ready
    .then((registration) => {
      registration.unregister();
    })
    .catch((err) => {
      console.log(err);
    });
}

export function register() {
  if (!swKey in navigator) {
    return;
  }

  window.addEventListener("load", () => {
    const swUrl = `/workbox.js`;

    registerServiceWorker(swUrl);
  });
}

こちらはservice-workerを登録/登録解除するjsを記述しています。/service-worker.jsが存在しているかどうかなどの確認は行わず、簡潔に記述しています。

そして重要なworkboxを利用したprecacheを行っている/workbox.jsはこちらです。

importScripts(
  "https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js"
);

const cacheTargets = [
  { url: "/", revision: null },
  { url: "/react.png", revision: null },
];

workbox.core.clientsClaim();

workbox.precaching.precacheAndRoute(cacheTargets, {});

const handler = workbox.precaching.createHandlerBoundToURL("/");
const navigationRoute = new workbox.routing.NavigationRoute(handler);
workbox.routing.registerRoute(navigationRoute);

上記で行っていることは

  • workboxをcdnからimport
  • clientsClaimしてすぐにコントロールできる状態に
  • cacheするファイルを定義
  • navigation requestの設定

です。

これでサーバーを立ち上げ、http://localhost:8081/にhttpリクエストすることでservice-workerがprecacheを行ってくれます。

precache後はオフラインでもコンテンツを表示することができるようになりました。

まとめ

今回は完結にworkboxを用いてprecacheしてみました。

service-workerはまだ理解できていない部分があるのでこまめに学習したいです。

以上です。