Search code examples
vue.jsvuejs2vue-routervitevue-router4

Lazy loading & loading states with vue-router, vite & vuejs 2.x


I'm migrating an old project from vue-cli to vite. I followed the migration guide and everything worked great, but there's something it's not working, or at least, not as intended, when I was using vue-cli I tried to implement the loading states as shown in their documentation but then I saw the following pull request explaining how to achieve the wanted behavior (with the downside of losing navigation guards on those routes).

Now, after migrating I noticed that neither the loading/error components are rendered at all, even setting a timeout really small, however, I see in the networking tab that my components are being loaded, but never rendered.

Do you have any suggestions of why might this occur?.

// This is the lazyLoadView that was working before migrating to vite.

function lazyLoadView(AsyncView) {
  const AsyncHandler = () => ({
    component: AsyncView,
    // A component to use while the component is loading.
    loading: import("./components/loaders/loader.vue").default,
    // A fallback component in case the timeout is exceeded
    // when loading the component.
    error: import("./components/loaders/error.vue").default,
    // Delay before showing the loading component.
    // Default: 200 (milliseconds).
    delay: 1,
    // Time before giving up trying to load the component.
    // Default: Infinity (milliseconds).
    timeout: 2,
  });
  return Promise.resolve({
    functional: true,
    render(h, { data, children }) {
      // Transparently pass any props or children
      // to the view component.
      return h(AsyncHandler, data, children);
    },
  });
}

And the routes I have:

const routes = [
    {
        path: "/foo/",
        component: () => lazyLoadView(import("./components/bar.vue")),
    }
]

Let me know if you find why might this be happening.


Solution

  • So I figured out:

    Looks like the loading & error components were also lazy loaded, they were skipped. So continued trying to obtain the main one until shown (that's why didn't render the loading besides of only showing a message).

    So in order to fix it I had to import them at the top and include them in the lazyLoadView like this:

    //These two imports at the top
    import loaderComp from "./components/loaders/loader.vue";
    import errorComp from "./components/loaders/error.vue";
    
    function lazyLoadView(AsyncView) {
      const AsyncHandler = () => ({
        component: AsyncView,
        // A component to use while the component is loading.
        // must NOT be lazy-loaded
        loading: loaderComp,
        // A fallback component in case the timeout is exceeded
        // when loading the component.
        // must NOT be lazy-loaded
        error: errorComp,
        // Delay before showing the loading component.
        // Default: 200 (milliseconds).
        delay: 1,
        // Time before giving up trying to load the component.
        // Default: Infinity (milliseconds).
        timeout: 2,
      });
      return Promise.resolve({
        functional: true,
        render(h, { data, children }) {
          // Transparently pass any props or children
          // to the view component.
          return h(AsyncHandler, data, children);
        },
      });
    }