URL: https://www.progressiverobot.com/vuejs-routing-data-fetching/

In the last post, More Advanced Routing with Vue, we covered using <^>Transitions<^> with <^>Vue Router<^>. This time we'll cover <^>Data Fetching<^> with <^>Vue Router<^>.

<^>Data Fetching<^> allows us to load asynchronous data in our routed components. We can also specify whether data is fetched before or after a component is loaded. Both of these strategies are equally viable but have different implementations. We'll cover both.

Vue Project Setup

vue illustration for: Vue Project Setup

Since this is yet another post about advanced Vue Router techniques, we'll assume you're already familiar with the basic setup. However, let's define a starting point that we'll use for the rest of the post:

				
					
# Yarn

$ yarn add vue-router



# npm

$ npm install vue-router --save

				
			
				
					
[label main.js]

import Vue from 'vue';

import VueRouter from 'vue-router';



import App from './App';

import Swamp from './components/Swamp';

import Gator from './components/Gator';



Vue.use(VueRouter);



const router = new VueRouter({

  routes: [

    { path: '/swamp', component: Swamp },

    { path: '/gator', component: Gator }

  ]

});



new Vue({

  render: h =&gt; h(App),

  router

}).$mount('#app')

				
			
				
					
[label App.vue]

&lt;template&gt;

  &lt;div id="app"&gt;

    &lt;div class="nav"&gt;

      &lt;router-link to="/swamp"&gt;Swamp&lt;/router-link&gt; |

      &lt;router-link to="/gator"&gt;Gator&lt;/router-link&gt;

    &lt;/div&gt;

    &lt;hr /&gt;

    &lt;div class="router-view"&gt;

      &lt;router-view&gt;&lt;/router-view&gt;

    &lt;/div&gt;

  &lt;/div&gt;

&lt;/template&gt;



&lt;script&gt;

export default { name: 'App' }

&lt;/script&gt;

				
			
				
					
[label components/Swamp.vue]

&lt;template&gt;

  &lt;div&gt;Welcome to the Swamp, {{ name }}!&lt;/div&gt;

&lt;/template&gt;



&lt;script&gt;

export default {

  name: 'Swamp',

  data() {

    return { name: null }

  },

}

&lt;/script&gt;

				
			
				
					
[label components/Gator.vue]

&lt;template&gt;

  &lt;div&gt;Howdy, Gator {{ name }}!&lt;/div&gt;

&lt;/template&gt;



&lt;script&gt;

export default {

  name: 'Gator',

  data() {

    return { name: null }

  }

}

&lt;/script&gt;

				
			

Data Fetching

Let's say we had a function that mocked a simple HTTP GET called pretendGet():

				
					
[label scripts/pretendGet.js]

export default (callback) =&gt; {

  setTimeout(() =&gt; {

    callback(null, 'Alice');

  }, 500);

}

				
			

After 500ms pretendGet() will return the string 'Alice' to the callback method callback. We'll use this to mock a server request in the following examples.

Fetching before navigation

Fetching <^>before<^> navigation allows us to ensure that our routed components have the data they need before being displayed to the user. In this approach we added a beforeRouteEnter method which <^>Vue Router<^> calls when the user requests to navigate to this component but before it has loaded. We also define a beforeRouteUpdate method which is called when the route changes. This is useful if you're fetching data related to a route parameter which is accessible via to.params.

				
					
[label components/Gator.vue]

import pretendGet from '../scripts/pretendGet';



export default {

  name: 'Gator',

  data() {

    return { name: null }

  },

  // Component not loaded yet

  beforeRouteEnter(to, from, next) {

    pretendGet((err, name) =&gt; {

      next(vm =&gt; vm.setName(err, name));

    });

  },

  // Component already loaded and route changes

  beforeRouteUpdate(to, from, next) {

    this.name = null;

    pretendGet((err, name) =&gt; {

      this.setName(err, name);

      next();

    });

  },

  methods: {

    setName(err, name) {

      if (err) {

        console.error(err);

      } else {

        this.name = name;

      }

    }

  }

}

				
			

Keep in mind that navigation will not happen until data is fetched. Because of this, it's a good idea to display some kind of progress bar or indicator that data is being fetched. If there's an error, it would also be a good idea to display that as well.

Fetching after navigation

Fetching <^>after<^> navigation is also a valid approach. In this case we'll utilize the created() [lifecycle hook](/vuejs/component-lifecycle/#creation-initialization) to call our data fetching method fetchName(). We'll also define a watch property on $route so we can call fetchName() whenever the route changes.

				
					
[label components/Swamp.vue]

import pretendGet from '../scripts/pretendGet';



export default {

  name: 'Swamp',

  data() {

    return { name: null }

  },

  created() {

    this.fetchName();

  },

  watch: {

    // Re-fetch when route changes

    '$route': 'fetchName'

  },

  methods: {

    fetchName() {

      pretendGet((err, name) =&gt; {

        if (err) {

          console.error(err);

        } else {

          this.name = name;

        }

      });

    }

  }

}

				
			

Keep in mind that with this approach we'll have to account for data not being ready when the component is first rendered. It's a good idea to include placeholders or skeleton placeholders along with indicators to let the user know that data is being fetched along with any errors that might occur.

Wrapping Up

<^>Vue Router<^> <^>Data Fetching<^> is a great way to ensure a smooth user experience for your components that rely on fetching data from external sources. Fetching before navigation is a good approach if you're a fan of using app-wide notifications or progress indicators. If you'd rather handle this kind of stuff on a component level then fetching after navigation might be the right approach for you. As always, make sure to read the docs!