How to display a "loading" animation while a lazy-loaded route component is being loaded?

You can use navigation guards to activate/deactivate a loading state that shows/hides a loading component:

If you would like to use something like "nprogress" you can do it like this:

http://jsfiddle.net/xgrjzsup/2669/

const router = new VueRouter({
  routes
})

router.beforeEach((to, from, next) => {
  NProgress.start()
  next()
})
router.afterEach(() => {
  NProgress.done()
})

Alternatively, if you want to show someting in-place:

http://jsfiddle.net/h4x8ebye/1/

Vue.component('loading',{ template: '<div>Loading!</div>'})

const router = new VueRouter({
  routes
})

const app = new Vue({
  data: { loading: false },
  router
}).$mount('#app')

router.beforeEach((to, from, next) => {
  app.loading = true
    next()
})

router.afterEach(() => {
  setTimeout(() => app.loading = false, 1500) // timeout for demo purposes
})

Then in the template:

<loading v-if="$root.loading"></loading>
  <router-view v-else></router-view>

That could also be easily encapsulated in a very small component instead of using the $root component for the loading state.


For what it's worth, I'll share what I ended up doing for my situation.

I'm using Vuex so it was easy to create an app-wide "loading" state which any component can access, but you can use whatever mechanism you want to share this state.

Simplified, it works like this:

function componentLoader(store, fn) {
  return () => {
    // (Vuex) Loading begins now
    store.commit('LOADING_BAR_TASK_BEGIN');

    // (Vuex) Call when loading is done
    const done = () => store.commit('LOADING_BAR_TASK_END');

    const promise = fn();
    promise.then(done, done);
    return promise;
  };
}

function createRoutes(store) {
  const load = fn => componentLoader(store, fn);

  return [
    {
      path: '/foo',
      component: load(() => import('./components/foo.vue')),
    },
    {
      path: '/bar',
      component: load(() => import('./components/bar.vue')),
    },
  ];
}

So all I have to do is wrap every () => import() by my load() function which takes care of setting the loading state. Loading is determined by observing the promise directly instead of relying on router-specific before/after hooks.