简体   繁体   中英

How to handle caching of index.html file with service worker?

I am currently working on a progressive web app using the create-react-app default service worker.

I am having issues with busting the cache when releasing a new version of one of our javascript chunks.

When building, the output javascript files use a contenthash to make sure that when content changes in the JS file, so does the file name. This successfully busts the cache when running WITHOUT a service worker.

However, when using the create-react-app service worker, all static assets including my index.html file is cached. This means that the old index.html is being served to users, which includes a <script> tag to my old, cached javascript file(s) instead of the new one with the updated contenthash .

I have ejected and modified the webpack.config.js WorkboxWebpackPlugin to exclude my index.html file:

 new WorkboxWebpackPlugin.GenerateSW({
      clientsClaim: true,
      exclude: [/\.map$/, /asset-manifest\.json$/, /index.html/],
      importWorkboxFrom: "cdn",
      navigateFallbackBlacklist: [
          // Exclude URLs starting with /_, as they're likely an API call
             new RegExp("^/_"),
          // Exclude URLs containing a dot, as they're likely a resource in
          // public/ and not a SPA route
            new RegExp("/[^/]+\\.[^/]+$")
          ]
        }),

And this seems to appropriately stop the caching of my index file, allowing it to include the updated main.[contenthash].js in its script tag. However, now I am aware that my PWA will not work offline as the index.html file is not served by the service worker and will not be served from an offline connection.

What is the best way to handle this? Full offline access isn't essential but would be nice to have, and am wondering how other developers are handling the 'busting' of the service worker cache for their index file without fully removing index.html from being cached by the service worker? Is there a more functional way to handle this change in the index.html regarding tags with content hashes?

Thank you

I worked on an app shell architecture where the service worker caches the app shell and returns it for subsequent requests. For Cache busting, I versioned the app shell also, like appshell-[hash].html, so next time when hash changes, the service worker will cache and serve the new app shell discarding the old one.

One way you could make it work is by using the "fallback to cache" caching strategy. Essentially, when document (eg index.html file) is requested you first try to load it from the network by forwarding the fetch request. However, if the fetch request fails (network is not available) you would then serve the request from the cache. You will also need to update your cached copy of index.html of course with each request to preserve to fresh copy.

This way, if the network is available — you will always get the fresh version of your index.html , if not, the latest known version will be served from the cache.

If you used create-react-app with cra-template-pwa (npx create-react-app my-app --template cra-template-pwa) then you can filter out the index.html when you precache and then register index.html so that it's network first.

import { NetworkFirst } from 'workbox-strategies'

// precacheAndRoute(self.__WB_MANIFEST)

const toPrecache = self.__WB_MANIFEST.filter(
  (file) => !file.url.includes('index.html'),
)

precacheAndRoute(toPrecache)

registerRoute(
  ({ url }) => url.pathname.includes('index.html'),
  new NetworkFirst(),
)

Then you need to remove the part where it routes all navigation requests to index.html

// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
// const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$')
// registerRoute(
//   // Return false to exempt requests from being fulfilled by index.html.
//   ({ request, url }) => {
//     // If this isn't a navigation, skip.
//     if (request.mode !== 'navigate') {
//       return false
//     } // If this is a URL that starts with /_, skip.

//     if (url.pathname.startsWith('/_')) {
//       return false
//     } // If this looks like a URL for a resource, because it contains // a file extension, skip.

//     if (url.pathname.match(fileExtensionRegexp)) {
//       return false
//     } // Return true to signal that we want to use the handler.

//     return true
//   },
//   createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html', true),
// )

The service worker is only enabled in the production environment, eg the output of npm run build. It's recommended that you do not enable an offline-first service worker in a development environment, as it can lead to frustration when previously cached assets are used and do not include the latest changes you've made locally.

From -https://create-react-app.dev/docs/making-a-progressive-web-app/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM