Search code examples
service-workerprogressive-web-appsworkboxdynamic-import

Retrying a failed dynamic import? (SPA/PWA)


Something that I'm running into with a project I'm working on is, I have a single page application. I'm handling browser navigation routing on the client-side, that lets me dynamically import some modules whenever a route is matched. My routing setup looks a bit like this:

router.setRoutes([
    {
        path: '/',
        component: 'app-main', // statically loaded
    },
    {
    path: '/posts',
        component: 'app-posts',
        action: () => { import('./app-posts.js').catch(() => Router.go('/offline');} // dynamically loaded
    },
    {
        path: '/offline', network
        component: 'app-offline', // also statically loaded
    }
]);

And here's a simple image of the 'app' for clarity:

A simple single page application that has a header, three links: main, posts and offline. And some body content

I'm caching the app shell in my service worker, which means that the main page, and the offline page get precached, and the posts page should get cached during runtime (once requested, so if the user clicks on the posts link)

So my precache manifest caches: main.js, offline.js, and my index.html.

Where I'm hitting a bump is:

  • The user loses network connection
  • The user tries to go to the posts page
  • The dynamic import for this may fail if it hasnt been requested and cached before (and the user will be redirected to the offline page)

But when my user gains network connectivity again, clicks the posts link, the dynamic import will still fail; I'm guessing because the browser dedupes dynamic imports.

Which is a huge shame, because my user has a network connection; this request should succeed! The only way I can figure out how to deal with this is to have the user reload the page, and request the posts page again.

So my question is, how should I go about this?


Solution

  • Solved it by checking if the user has network connection before trying to do the import like this:

    function handleRoute(url) {
      if('onLine' in navigator) {
        if(navigator.onLine) {
          // user is online, safe to import
          import(url);
        } else {
          // user is offline, don't even try to import -> straight to `/offline`
          Router.go('/offline');
        }
      } else {
        // incase the browser doesnt support `navigator.onLine`, try to import anyway
        import(url);
      }
    }
    

    It feels like a slightly hacky way to do it, but then again browser support for navigator.onLine is pretty good.

    For more information on retrying failed dynamic imports, I found this issue in the tc39/proposal-dynamic-import github repo.