Skip to content
This repository has been archived by the owner on Jan 23, 2021. It is now read-only.

cache index.html for offline, but refresh on new deploy #356

Open
phun-ky opened this issue May 28, 2018 · 2 comments
Open

cache index.html for offline, but refresh on new deploy #356

phun-ky opened this issue May 28, 2018 · 2 comments

Comments

@phun-ky
Copy link

phun-ky commented May 28, 2018

I am currently using the sw-precache-webpack-plugin with this config: (yes, another project, this is just to show the config, since some of the configuration options are passed to sw-precache + sw-toolbox). This is a SPA.

new SWPrecacheWebpackPlugin({
  cacheId: 'project',
  filename: 'project-sw.js',
  minify: true,
  maximumFileSizeToCacheInBytes: 3500000,
  filepath: path.join(assetsDir, '/project-sw.js'),
  staticFileGlobs: ['dist/assets/index.html'],
  mergeStaticsConfig: true,
  runtimeCaching: [
    {
      urlPattern: /\/api\//,
      handler: 'networkFirst'
    }
  ],
  staticFileGlobsIgnorePatterns: [/project-sw\.js$/i],
  stripPrefix: 'dist/assets/',
  navigateFallback: '/index.html',
  navigateFallbackWhitelist: [/^((?!adomain|anotherdomain).)*$/]
})

We initialize the service worker like this in an attempt to always load the new service worker when we deploy:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker
    .register('/project-sw.js?build=' + __webpack_hash__);
}

Operation-wise, it works as intended. But, we cache index.html for offline usage, and this also caches the reference for the app file, app.<__webpack_hash__>.js, leading to the wrong bundles being requested.

Scenario (from clean slate):

  1. Open /index.html
  2. app.fd9e432.js is loaded
  3. service worker is loaded with /project-sw.js?build=fd9e432

.. next visit after redeploy (new hash):

  1. Open /index.html (which is cached by the previous service worker)
  2. app.fd9e432.js is loaded (same as last time)
  3. The correct service worker is loaded with /project-sw.js?build=fd9e432
  4. Goes to /newpath, webpack tries to load bundle with old hash: newpath.fd9e432.js and gets a 404

The cache headers on index.html and project-sw.js is the same (no cache):

cache-control: no-store, no-cache, must-revalidate, proxy-revalidate
etag: W/"241e-6NP8wbah/2UUxPQ4NPT2uJkYld8"
expires: 0
pragma: no-cache

Is there a configuration I am missing here, or what am I doing wrong?

@phun-ky
Copy link
Author

phun-ky commented May 29, 2018

Hm 🦆 , is it perhaps possible to avoid the issue by not using hashes in the bundles? That app.<hash>.js -> app.js et al? Then the reference would be sane (we're not caching static files on the server but in the service worker)?

@phun-ky
Copy link
Author

phun-ky commented Jun 6, 2018

Update, the issue has a fix, but I don't think the underlying issue is resolved. Here is how we force a reload of the service worker when new content is detected: (It's a workaround at best)

if ('serviceWorker' in navigator) {
  navigator.serviceWorker
    .register('/project-sw.js?build=' + __webpack_hash__)
    .then(function(reg) {
      // registration worked
      console.info('Service Worker Registration succeeded. Scope is ' + reg.scope);
      // updatefound is fired if service-worker.js changes.
      reg.onupdatefound = function() {
        // The updatefound event implies that reg.installing is set; see
        // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
        var installingWorker = reg.installing;

        installingWorker.onstatechange = function() {
          switch (installingWorker.state) {
            case 'installed':
              if (navigator.serviceWorker.controller) {
                // At this point, the old content will have been purged and the fresh content will
                // have been added to the cache.
                // It's the perfect time to display a "New content is available; please refresh."
                // message in the page's interface.
                // This is also a nice place to put major/minor version logic to inform the user of new features
                console.info('New or updated content is available.');
                window.location.reload(true);
              } else {
                // At this point, everything has been precached.
                // It's the perfect time to display a "Content is cached for offline use." message.
                console.info('Content is now available offline!');
              }
              break;

            case 'redundant':
              console.error('The installing service worker became redundant.');
              break;
          }
        };
      };
    })
    .catch(function(error) {
      // registration failed
      console.error('Service Worker Registration failed with ' + error);
    });
}

manuelkiessling added a commit to manuelkiessling/nodebeginner.org that referenced this issue Sep 17, 2018
…blem.

The problem was as follows:

- Webpack builds the app js and stores it as e.g. app.hash-1.js
- When requesting the server, the client initializes the service worker,
  which in turn stores /sw-precache-appshell in the cache; in
  sw-precache-appshell's HTML, app.hash-1.js is referenced; also
  app.hash-1.js is stored in the cache
- On subsequent soft-loads, the content of sw-precache-appshell is
  served from the cache
- The app is changed and rebuilt, resulting in file app.hash-2.js; file
  app.hash-1.js is no longer served by the backend
- Another soft-load results in the cached version of
  sw-precache-appshell being served, which still references
  app.hash-1.js - as this is still in the cache and served from there,
  all works fine
- The service worker fetches itself at /service-worker.js, realizes that
  the app js has changed, fetches app.hash-2.js, and stores that in the
  cache, removing app.hash-1.js from the cache; however, the service
  worker does not realize that /sw-precache-appshell changes, which
  could be a bug in sw-prefetch; thus the "old" version of
  sw-precache-appshell which still refers to the "old" app.hash-1.js,
  remains in the cache
- Doing a soft-load again, the service worker serves the old
  sw-precache-appshell content, where app.hash-1.js is now referenced;
  as this is available neither from cache nor from the backend, the app
  stops working

The solution here is to NOT use any kind of hash-based file naming for
WebPack-generated files, but simply use a static name; this way, the
reference is always to "app.js" which always matches, while the service-
worker still knows when to fetch new file content and update the cache.

See GoogleChromeLabs/sw-precache#356
See api-platform/website#62
See gatsbyjs/gatsby#4636
See GoogleChromeLabs/sw-precache#269 (comment)
manuelkiessling added a commit to manuelkiessling/nodebeginner.org that referenced this issue Sep 17, 2018
…blem.

The problem was as follows:

- Webpack builds the app js and stores it as e.g. app.hash-1.js
- When requesting the server, the client initializes the service worker,
  which in turn stores /sw-precache-appshell in the cache; in
  sw-precache-appshell's HTML, app.hash-1.js is referenced; also
  app.hash-1.js is stored in the cache
- On subsequent soft-loads, the content of sw-precache-appshell is
  served from the cache
- The app is changed and rebuilt, resulting in file app.hash-2.js; file
  app.hash-1.js is no longer served by the backend
- Another soft-load results in the cached version of
  sw-precache-appshell being served, which still references
  app.hash-1.js - as this is still in the cache and served from there,
  all works fine
- The service worker fetches itself at /service-worker.js, realizes that
  the app js has changed, fetches app.hash-2.js, and stores that in the
  cache, removing app.hash-1.js from the cache; however, the service
  worker does not realize that /sw-precache-appshell changes, which
  could be a bug in sw-prefetch; thus the "old" version of
  sw-precache-appshell which still refers to the "old" app.hash-1.js,
  remains in the cache
- Doing a soft-load again, the service worker serves the old
  sw-precache-appshell content, where app.hash-1.js is now referenced;
  as this is available neither from cache nor from the backend, the app
  stops working

The solution here is to NOT use any kind of hash-based file naming for
WebPack-generated files, but simply use a static name; this way, the
reference is always to "app.js" which always matches, while the service-
worker still knows when to fetch new file content and update the cache.

See GoogleChromeLabs/sw-precache#356
See api-platform/website#62
See gatsbyjs/gatsby#4636
See GoogleChromeLabs/sw-precache#269 (comment)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant