vue.jsvue-routergridsome

Show spinner (preloader/loading indicator) whenever page changes and hide when all assets are loaded in Vue Gridsome


I am using Gridsome (Vue static site generator with Vue Router) and I've created a preloader in index.html, its a simple div that covers everything. In index.html I also added this JS code to hide the preloader when everything loads

window.onload = function() {
  document.getElementById('preloader').style.display = 'none';
};

This works only for the initial load, but when changing pages I am having trouble showing it and hiding it again.

I've tried to add this to my Layout component's beforeDestroy() hook to show the preloader again

beforeDestroy() {
  this.preloader.style.display = 'block';
}

which shows it successfully when the route is changed, but then if I add the hiding logic in mounted() like this

mounted() {
  this.preloader.style.display = 'none';
}

the preloader is never showed in the first place.

I was unable to find any resources about this kind of loading indicators, all I can find are one's for async calls like axios or fetch. I've created preloaders before in static HTML files, but never in SPAs. Can someone please push me in the right direction? Even googling keywords will help


Solution

  • you can use vuex with this case.

    first, add your state src/main.js

    import DefaultLayout from "~/layouts/Default.vue";
    import Vuex from "vuex";
    
    export default function(Vue, { appOptions }) {
      Vue.component("Layout", DefaultLayout);
      Vue.use(Vuex);
    
      appOptions.store = new Vuex.Store({
        state: {
          loading: false,
        },
        mutations: {
          on(state) {
            state.loading = true;
          },
          off(state) {
            state.loading = false;
          },
        },
      });
    }
    

    second, add spinner to ./src/layouts/Default.vue

    <template>
      <div class="layout">
        // add your spinner here or another
        <div v-if="$store.state.loading">loading</div>
        <slot />
      </div>
    </template>
    

    finally, add commit code pages, templete, or components. like below.

    <script>
    export default {
      created() {
        // commit("on") first
        this.$store.commit("on");
    
        // commit("off") last, after fetch data or more.
        this.$store.commit("off");
      },
    };
    </script>