Vue Router

Author: MiloŇ° Medan
Posted on 13 April, 2018
Vue Router is easy to learn and very powerful tool which give us large number of options.

In this post you will be able to see how to create entire new Vue project and how to properly install and create Vue Router in order to make SPA (single-page application). As a first step download and install the latest version of Node.js from Node Js Website. Beside this, we should install vue-cli (Vue command line interface). The main reason for installing vue-cli is to help us install Vue project templates.

Installation

To perform vue-cli installation, write this line in your terminal:

npm install -g vue-cli

After we installed vue-cli we can initialize vue project with following templates:

  • webpack
  • webpack-simple
  • browserify
  • browserify-simple
  • pwa
  • simple

Among this six templates, everyone should be able to find suitable one. For more details about Vue templates and their features, visit this website.

In this blog post we will use webpack-simple template:

vue init webpack-simple my-vue-project

After a while you'll be prompt to answer several questions:

? Project name (my-vue-project) // name for your further project
? Project description (A Vue.js description) // descripe your project
? Author // author name
? Lincence (MIT) // enter
? Use sass (y/N) // press enter for default answer (in this case SASS won't be used)

After setting up basic project properties, few lines below you will see that vue-cli generated new project inside folder called my-vue-project. Down there we will get few more instructions on what to do in order to successfully finish project installation.

cd my-vue-project
npm install
npm run dev

Basics

As a result, browser will start "Welcome page". Now, we can open our project from any text editor - IDE (in my case it's PHP storm). Inside src folder, there are two files which will be interesting for our future development.

  1. main.js - inside this file vue-cli automatically created VueJS object
  2. App.js - in this file we have code responsible for rendering "Welcome Page"

Feel free to remove entire code, from App.js in order to start from scratch. As a result, we'll get blank page in our browser.

Every Vue component should have template tags at least. Script and style tags are optional.

Also it is very important to wrap whole HTML code into one (root) HTML element, otherwise our Vue component won't work.

Let's start with simple example:

File: my-vue-project/src/App.js
<template>
    <div>
        <p>HELLO FROM App.js TEMPLATE</p>
    </div>
</template>

Vue Router installation

Open terminal, and navigate to project folder. Then write following line:

npm install vue-router

There is a few ways to install Vue Router but this is the most common one. For more details about installation visit this page. To check weather we successfully installed vue-router, open package.json file from project directory. In the dependenciesobject you have to have vue-router property.

Defining Routes

In main.js file import VueRouter object from vue-router package, and make sure that Vue object is going to use it in the right way.

import VueRouter from 'vue-router';
Vue.use(VueRouter);

Inisde src folder, create new folder - routes. Than in routes folder create new file routes.js.

File: my-vue-project/src/routes/routes.js
import Home from '../components/Home.vue';
import Users from '../components/Users.vue';
import Details from '../components/Details.vue';

export const routes = [
    {path: '/', component: Home},
    {path: '/users', component: Users},
    {path: '/details', component: Details}
];

When we visit url http://localhost:8080, router will call Home.vue component defined in routes variable. Similar will happen if we visit http://localhost:8080/users, router will call Users.vue. Exactly the same will happen with Details.vue component.

Than, go back to main.js and import const routes from routes.js, which will be used in VueRouter object. At the end, entire main.js file will have following structure:

File: my-vue-project/src/main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router';
import {routes} from './routes/routes.js';

Vue.use(VueRouter);

const router = new VueRouter({
    mode: 'history',
    routes
});

new Vue({
    el: '#app',
    router,
    render: h => h(App)
});

To make router work we will need to modify App.js file and add router-view component.

File: my-vue-project/src/App.js
<template>
    <div>
        <router-view></router-view>
    </div>
</template>

This component gives us ability to wrap another component inside, and display its content on the page.

There is another crucial component which also plays vital role in SPA applications. Its name isrouter-link. This component, allows us to move between different pages.

File: my-vue-project/src/components/Home.vue
<template>
    <div>
        <p>HELLO FROM HOME PAGE</p>
        <router-link to="/users" tag="button" class="btn btn-primary" style="color: black;">
            button
        </router-link>
    </div>
</template>

As you see router-link can have attributes which help us to determine more closely content inside tag. For instance, make some styling, add class or id. Within help tag attribute, we define, type of HTML element. Nevertheless, from my point of view, key role plays to attribute, which main task is to properly navigate us to another page. In case above, we will get button HTML element. As a result, if we click on that button, we will be redirect to Users page. We can also give a name to our route, but we have to do some changes inside routes.js file. For that matter, to have to be an object.

File: my-vue-project/src/routes/routes.js
export const routes = [
    {path: '/', component: Home, name: 'home'},
    {path: '/users', component: Users, name: 'users'},
    {path: '/details', component: Details, name: 'details'}
];
File: my-vue-project/src/components/Home.vue
<template>
    <div>
        <p>HELLO FROM HOME PAGE</p>
        <router-link :to="{name: 'users'}" tag="button" class="btn btn-primary" style="color: black;">
            button
        </router-link>
    </div>
</template>

Parameters and Queries

File: my-vue-project/src/components/Users.vue
<template>
    <div>
        <p>HELLO FROM USER PAGE</p>
        <ul class="list-group">
            <router-link :to="{name: 'details', params: {id: 1}}" tag="li" class="list-group-item">User 1</router-link>
            <router-link :to="{name: 'details', params: {id: 2}}" tag="li" class="list-group-item">User 2</router-link>
            <router-link :to="{name: 'details', params: {id: 3}}" tag="li" class="list-group-item">User 3</router-link>
        </ul>
    </div>
</template>

We got a list of three users. Each of them is created inside router-link, and links to Details page. You already noticed that to attribute has name property that chooses path by name, and params property which is another nested object with id property. Before we decide to click on one of them, you must define id in routes.js.

File: my-vue-project/src/routes/routes.js
import Home from '../components/Home.vue';
import Users from '../components/Users.vue';
import Details from '../components/Details.vue';

export const routes = [
    {path: '/', component: Home},
    {path: '/users', component: Users, name: 'users'},
    {path: '/users/:id', component: Details, name: 'details'}
];
File: my-vue-project/src/components/Details.vue
<template>
    <p>HELLO FROM DETAILS PAGE</p>
    <div>
        <p>{{ id }}</p>
        <router-link to="/users" tag="button">Back to user page</router-link>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                id: this.$route.params.id
            }
        }
    }
</script>

It is important to know that there is possibility to pass query parameters too. It can be quite useful feature. Let's do some example:

File: my-vue-project/src/components/Users.vue
<template>
    <div>
        <p>HELLO FROM USER PAGE</p>
        <ul class="list-group">
            <router-link :to="{name: 'details', params: {id: 1}}" tag="li" class="list-group-item">User 1</router-link>
            <router-link :to="{name: 'details', params: {id: 2}}" tag="li" class="list-group-item">User 2</router-link>
            <router-link :to="{name: 'details', params: {id: 3}, query: {country:'England', city: 'London'}}" tag="li" class="list-group-item">User 3</router-link>
        </ul>
    </div>
</template>

We have to set query as object property, which has another object as value. It is standard form for passing queries through url. If you clicked on one of users from the list, you will be navigated to Details page.

Nested Routes

At this point, I would like to show you one more great feature called Nested routes, which I believe you will need to use very often in order to make complex page structure. Let's start to modifying our routes:

File: my-vue-project/src/routes/routes.js
import Home from '../components/Home.vue';
import Users from '../components/Users.vue';
import Details from '../components/Details.vue';
import Edit from '../components/Edit.vue';

export const routes = [
    {path: '/', component: Home},
    {path: '/users', component: Users, name: 'users'},
    {
        path: '/users/:id', component: Details, name: 'details', children: [
            {path: '/edit', component: Edit, name: 'edit'}
        ]
    }
];

We added children property to one of our routes. Now we must make some changes inside Detail.vue component. We will add new <router-link> and <router-view>.

File: my-vue-project/src/components/Details.vue
<template>
    <div>
        <p>HELLO FROM DETAILS PAGE</p>
        <p>{{ id }}</p>
        <router-link :to="{name: 'edit'}" tag="button">Edit this user</router-link>
        <router-link to="/users" tag="button">Back to user page</router-link>
        <router-view></router-view>
    </div>
</template>

In this case <router-view> inside Details.vue component is an entry point for Edit.vue component. In other words Edit.vue component will be loaded within Details.vue component, below "Edit this user" and "Back to user page" buttons.

Navigation Guards

This example will represent one nice feature provided by Vue Router - Navigation Guards. Let's assume that we want to edit some information about user. For that operation we need to have certain permission. It is not clever idea to enable anyone to visit that page, without meeting certain condition. For that purpose one of the security meassure, can be Navigation Guards, which will force user to load page only if some condition is satisfied. For instance, checking previous path inside URL.

File: my-vue-project/src/routes/routes.js
import Home from '../components/Home.vue';
import Users from '../components/Users.vue';
import Details from '../components/Details.vue';
import Edit from '../components/Edit.vue';

export const routes = [
    {path: '/', component: Home},
    {path: '/users', component: Users, name: 'users'},
    {
        path: '/users/:id', component: Details, name: 'details', children: [
            {
                path: '/edit', component: Edit, name: 'edit', beforeEnter(from, to, next) {
                    console.log(to); // Take a look what you can find in this object
                    console.log(from); // Take a look what you can find in this object
                    if (to.name === 'details') {
                        // If previous page had path ..../details
                        next(); // next() is called and allow us to visit another page
                    } else {
                        // Otherwise, our journy will be stoped.
                        console.log('You can not access to this page');
                    }
                }
            }
        ]
    }
];

There are three places, where we can define is user allowed to visit certain page. First one is inside main.js, where we use router.beforeEach((to, from, next) => {}), and check every page change. The most common case is when we define beforeEnter((to, from, next) => {})) inside route object. At last we can define guards inside component, using beforeRouterEnter((to, from, next) => {}). In that case, beforeRouterEnter function is invoked inside life cycle hook.

File: my-vue-project/src/components/Details.vue
<template>
    <div>
        <p>HELLO FROM DETAILS PAGE</p>
        <p>{{id}}</p>
        <router-link :to="{name: 'edit'}" tag="button">Edit this user</router-link>
        <router-link to="/users" tag="button">Back to user page</router-link>
        <router-view></router-view>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                id: this.$route.params.id
            }
        },
        beforeRouteLeave(to, from, next) {
            console.log(to);
            console.log(from);
            next();
        }
    }
</script>

More information about Navigation guards, you can find on this website.

Router Redirect

At the end, I would like to show you one more crucial feature, which you will use quite often. User can pass anything through URL, and sometimes it is not matched with our routes. In that case we can define special path with asterisk ( * ) and it will handle all non-define routes.

File: my-vue-project/src/routes/routes.js
import Home from '../components/Home.vue';
import Users from '../components/Users.vue';
import Details from '../components/Details.vue';
import Edit from '../components/Edit.vue';

export const routes = [
    {path: '/', component: Home},
    {path: '/users', component: Users, name: 'users'},
    {
        path: '/users/:id', component: Details, name: 'details', children: [
            {
                path: '/edit', component: Edit, name: 'edit', beforeEnter(from, to, next) {
                    console.log(to); // Take a look what you can find in this object
                    console.log(from); // Take a look what you can find in this object
                    if (to.name === 'details') {
                        // If previous page had path ..../details
                        next(); // next() is called and allow us to visit another page
                    } else {
                        // Otherwise, our journy will be stoped.
                        console.log('You can not access to this page');
                    }
                }
            }
        ]
    },
    // This line is added
    {path: '*', redirect: '/users'}
];

Then try to enter anything you want into URL, and you will be redirect to Users.vue component, and URL will look like http://localhost:8080/users.

Conclude

Vue Router is one of the core VueJs features, and it can significantly improve our application. Also there are many other great features and all about them you can find on this website.