12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ServiceWorker のインストールが終わるまで初期化を遅延する & ServiceWorker の更新をユーザーに通知する

Last updated at Posted at 2018-06-07

やりたいこと

SEO を気にしないモダンブラウザのウェブアプリでは、初回ロード時にServiceWorker の初期化前に始まったリクエストが、タイミング次第でキャッシュが構築されたりされなかったりする問題がある。同じ動作になることを保証するために、 ServiceWorker の初期化が終わってからJSの読み込みなどを行いたい。

ついでに、裏側でServiceWorkerの更新があったらユーザーにモーダルなどで通知したい。

前提として、生存時間が長い富豪的なSPAを想定している。

コード

あらゆるJS含むリソースの初期化が前提に出来ないので、フレームワークを使わず、HTMLのインライン一筆書きした。

  • /sw.js - serviceWorker
  • /main.js - 本来読み込みたいコード

es modules がないブラウザは nomodule を実行して警告する。

<html>

<head>
  <title>PWA</title>
  <meta charset="utf8">
</head>

<body>
  <div class=root>
    Loading...
  </div>
  <script nomodule type=text/javascript>
document.body.innerHTML = "Please use modern browser";
  </script>

  <script type=module>
function setupBrowserFS() {
  return new Promise(resolve => {
    BrowserFS.configure({ fs: "IndexedDB", options: {} }, err => {
      if (err) {
        throw err
      }
      resolve()
    })
  })
}

async function setupServiceWorker() {
  if (navigator.serviceWorker == null) {
    throw new Error('Your browser can not use serviceWorker')
  }

  let installed = !!navigator.serviceWorker.controller
  navigator.serviceWorker.addEventListener('controllerchange', () => {
    if (installed) {
      const modal = document.createElement('div')
      modal.innerHTML = `
        <div style='position: absolute; outline: 1px solid black; right: 10px; bottom: 10px; width: 350px; height: 80px'>
          <div>New version available!</div>
          <span>It will be applied from the next</span> - <button onclick="location.reload()">Reload</button>
        </div>
      `
      document.body.appendChild(modal)
    }
  });

  const reg = await navigator.serviceWorker.register('/sw.js');
  await navigator.serviceWorker.ready
  installed = true

  setInterval(() => {
    reg.update();
  }, 60 * 1000);
}

;(async () => {
  const el = document.querySelector('.root')
  try {
    // SW
    el.innerHTML = 'Checking service worker ...'
    await setupServiceWorker()

    // Run
    el.innerHTML = 'Loading...'
    await import('/main.js')
  } catch (e) {
    el.innerHTML = 'Something wrong: ' + e.message
  }
})()
  </script>
</body>

</html>

このコードでは、60s に一回 service worker の更新確認を行っている。更新があったかどうかのヘッダのチェックだけなので軽いはずだが、なんだかんだでリクエスト走るし、なんだかんだでリロードされると思うので、実際は15分に1回ぐらいでいいと思う。

気をつける点は、この index.html を offline cache に突っ込んでる場合、このコードの変更自体が適用されるのが次回の更新、更新が確認できるのが次の次になることだろうか。

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?