Defining routes
To separate out the routes into different files within our application, we can firstly create a file undersrc/components/usernameduser.routes.js. Each time we have a different feature set (that requires routes), we can create our own*.routes.jsfile that can be imported into the router'sindex.js.
For now, we can just export a new empty array:
export const userRoutes = [];
We can then add the routes to ourindex.js(even though we have none defined yet):
import { userRoutes } from '../components/user/user.routes';
const routes = [...userRoutes];
We're using the ES2015+ spread operator, which allows us to use each object in the array instead of the array itself.
To then initialize the router, we can then create a newVueRouterand pass along the routes, as follows:
const router = new VueRouter({
// This is ES2015+ shorthand for routes: routes
routes,
});
Finally, let's export the router so that it can be used inside our main Vue instance:
export default router;
Insidemain.js, let's import the router and add it to the instance, as shown:
import Vue from 'vue';
import App from './App.vue';
import router from './router';
new Vue({
el: '#app',
router,
render: h => h(App),
});
Creating the UserList route
The first section of our application will be a home page that displays a list of users from an API. We've used this example in the past, so you should be familiar with the steps involved. Let's create a new component undersrc/components/usernamedUserList.vue.
The component will look something like this:
<template>
<ul>
<li v-for="user in users" :key="user.id">
{{user.name}}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
users: [
{
id: 1,
name: 'Leanne Graham',
}
],
};
},
};
</script>
Feel free to add your own test data at this point. We'll be requesting this data from the API momentarily.
As we've created our component, we can then add a route touser.routes.js, which displays this component whenever the'/'(or a path of your choice) is activated:
import UserList from './UserList';
export const userRoutes = [{ path: '/', component: UserList }];
In order to show this route, we need to updateApp.vueto subsequently inject the content into arouter-viewnode. Let's updateApp.vueto handle this:
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
Our application should then display a single user. Let's create an HTTP utility to get data from an API.
Getting data from an API
Create a new file undersrc/utilsnamedapi.js. This will be used to create a base instance ofAxios, which we can then perform HTTP requests on:
import axios from 'axios';
export const API = axios.create({
baseURL: `https://jsonplaceholder.typicode.com/`
})
We can then use thebeforeRouteEnterNavigation Guard to get user data whenever someone navigates to the'/'route:
<template>
<ul>
<li v-for="user in users" :key="user.id">
{{user.name}}
</li>
</ul>
</template>
<script>
import { API } from '../../utils/api';
export default {
data() {
return {
users: [],
};
},
beforeRouteEnter(to, from, next) {
API.get(`users`)
.then(response => next(vm => (vm.users = response.data)))
.catch(error => next(error));
},
};
</script>
We then find that we get a list of users on screen, as illustrated in the following screenshot, each represented as a different list item. The next step is to create adetailcomponent, register the detail route, and find a way to link to that route:
Creating a detail page
In order to create a detail page, we can createUserDetail.vueand follow stepssimilarto the previous component:
<template>
<div class="container">
<div class="user">
<div class="user__name">
<h1>{{userInfo.name}}</h1>
<p>Person ID {{$route.params.userId}}</p>
<p>Username: {{userInfo.username}}</p>
<p>Email: {{userInfo.email}}</p>
</div>
<div class="user__address" v-if="userInfo && userInfo.address">
<h1>Address</h1>
<p>Street: {{userInfo.address.street}}</p>
<p>Suite: {{userInfo.address.suite}}</p>
<p>City: {{userInfo.address.city}}</p>
<p>Zipcode: {{userInfo.address.zipcode}}</p>
<p>Lat: {{userInfo.address.geo.lat}} Lng:
{{userInfo.address.geo.lng}} </p>
</div>
<div class="user__other" >
<h1>Other</h1>
<p>Phone: {{userInfo.phone}}</p>
<p>Website: {{userInfo.website}}</p>
<p v-if="userInfo && userInfo.company">Company:
{{userInfo.company.name}}</p>
</div>
</div>
</div>
</template>
<script>
import { API } from '../../utils/api';
export default {
data() {
return {
userInfo: {},
};
},
beforeRouteEnter(to, from, next) {
next(vm =>
API.get(`users/${to.params.userId}`)
.then(response => (vm.userInfo = response.data))
.catch(err => console.error(err))
)
},
};
</script>
<style>
.container {
line-height: 2.5em;
text-align: center;
}
</style>
As there should never be more than one user inside of our detail page, theuserInfovariable has been created as a JavaScript object rather than an array.
We can then add the new component to ouruser.routes.js:
import UserList from './UserList';
import UserDetail from './UserDetail';
export const userRoutes = [
{ path: '/', component: UserList },
{ path: '/:userId', component: UserDetail },
];
In order to link to this component, we can addrouter-linkwithin ourUserListcomponent:
<template>
<ul>
<li v-for="user in users" :key="user.id">
<router-link :to="{ path: `/${user.id}` }">
{{user.name}}
</router-link>
</li>
</ul>
</template>
If we then take a look in our browser we can see that there is only one user listed with the information underneath coming from the user detail linked to that one user:
Child routes
We can also access posts from our API, and as a result, we can display both the posts' information alongside our user information. Let's create a new component namedUserPosts.vue:
<template>
<div>
<ul>
<li v-for="post in posts" :key="post.id">{{post.title}}</li>
</ul>
</div>
</template>
<script>
import { API } from '../../utils/api';
export default {
data() {
return {
posts: [],
};
},
beforeRouteEnter(to, from, next) {
next(vm =>
API.get(`posts?userId=${to.params.userId}`)
.then(response => (vm.posts = response.data))
.catch(err => console.error(err))
)
},
};
</script>
This allows us to get posts based on ouruserIdroute parameter. In order to display this component as a child view, we'll need to register it as such within theuser.routes.js:
import UserList from './UserList';
import UserDetail from './UserDetail';
import UserPosts from './UserPosts';
export const userRoutes = [
{ path: '/', component: UserList },
{
path: '/:userId',
component: UserDetail,
children: [{ path: '/:userId', component: UserPosts }],
},
];
We can then add another<router-view>tag inside ourUserDetail.vuecomponent to display the child route. The template now looks like this:
<template>
<div class="container">
<div class="user">
// Omitted
</div>
<div class="posts">
<h1>Posts</h1>
<router-view></router-view>
</div>
</div>
</template>
To top it off, we've also added some styles that display the user information on the left and posts on the right:
<style>
.container {
line-height: 2.5em;
text-align: center;
}
.user {
display: inline-block;
width: 49%;
}
.posts {
vertical-align: top;
display: inline-block;
width: 49%;
}
ul {
list-style-type: none;
}
</style>
If we then head to our browser, we can see how the data appears just as we had planned, with the user information displaying on the left and the posts on the right:
Ta-da! We've now created a Vue application with multiple routes, child routes, parameters, and more!
Summary
In this section, we learned about the Vue Router and how we can use it to create Single Page Applications. As a result, we covered everything from initializing the router plugin to defining routes, components, Navigation Guards, and much more. We now have the necessary knowledge to create Vue applications that scale past a singular component.
Now that we have expanded our knowledge and understand how to use the Vue Router, we can move on to handling state management withVuexin the next chapter.