Vue Router Basics

45. Why Use the Vue Router?

If you want to have multiple URL endpoints in a website (like x.com, x.com/about, x.com/account) we can use a Vue router to insert particular components in the main App.vue component based in the URL that was typed in.

46. Router Setup for New Projects

In terminal, create a new Vue project using the Vue CLI.

$ vue create 6-ninja-jobs

  • Manually select features
  • Deselect Linter, select router, press enter
  • Use version 3
  • Select Y to use history mode for the router
  • Choose ‘In dedicated config files’
  • Select No to save as a preset for future projects

Open project in VSC.

In package.json we will see that we have a new router dependency.

Also in the src folder we have a subdirectory named router, and in it is an index.js file that is used to set up all of the routes.

In index.js we have a routes array.  Each element in the array has 3 properties: path, name, and component.

Currently the About page uses ‘lazy loading’ to load the AboutView component.  Modify the About array entry to load the About View like the HomeView component is loaded.

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    component: AboutView
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

Notice that a router constant is created using the routes array and is exported from the file.

In src/main.js we import the router object and use the router when we create the app.

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')

In App.vue we see the template use router-link and router-view tags.

<template>
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </nav>
  <router-view/>
</template>

The router-link elements specify routes and the router-view element is where the router components HomeView.vue or AboutView.vue will be loaded.

47. Router Links

As mentioned earlier, in the App.vue template we use <router-link> elements rather than <a> elements.  This is because we want Vue to intercept the click events on the links and use the router to display the appropriate content.

When Vue constructs the HTML for the page, it actually replaces the <router-link> elements with <a> elements and adds router-link-active and router-link-exact-active classes to the link that is currently active. Since the browser is initially on the home page with path “/”, then the Home component is loaded in the router-view element, and the Home link has these two classes.  We can see at the bottom of App.vue a style rule that changes the color of the active link to green.

nav a.router-link-exact-active {
  color: #42b983;
}

We can also data bind an object to the to attribute of the router-link element.

<router-link :to="{ name: 'about' }">About</router-link>
Binding the to attribute to the name of the route rather than the path is preferable since later we may want to change the path to say “/about-us”.  In this case, we only have to change the path in src/router/index.js.

Last, we clean up the project and remove the content that we don’t need.

First, delete the HelloWorld.vue component in the components directory.  Then in HomeView.vue, delete the HelloWorld component and add some lorem ipsum text.  We can add 100 words of lorem ipsum text by typing lorem100 and pressing enter.  HomeView.vue should look as follows.

<template>
  <div class="home">
    <h1> Home page</h1>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit magnam totam officia expedita quae omnis nobis labore consequuntur maiores dignissimos quas facilis fugiat dolores, iusto in, maxime officiis mollitia distinctio animi nihil accusantium minus voluptates dicta. Assumenda, eaque optio voluptatum porro, incidunt velit aliquam consequatur sequi laboriosam autem praesentium ratione nemo! Totam enim odit, fugit voluptas ipsum tempora dicta cum consectetur omnis illo. Rem consequatur repudiandae possimus velit commodi iusto natus, perspiciatis, nulla, nesciunt eius asperiores. Quidem dolores placeat voluptatum, voluptate temporibus cumque odio facere, dolorum eveniet dolorem in blanditiis nulla enim natus incidunt, doloribus distinctio aut laboriosam ipsum recusandae?</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit magnam totam officia expedita quae omnis nobis labore consequuntur maiores dignissimos quas facilis fugiat dolores, iusto in, maxime officiis mollitia distinctio animi nihil accusantium minus voluptates dicta. Assumenda, eaque optio voluptatum porro, incidunt velit aliquam consequatur sequi laboriosam autem praesentium ratione nemo! Totam enim odit, fugit voluptas ipsum tempora dicta cum consectetur omnis illo. Rem consequatur repudiandae possimus velit commodi iusto natus, perspiciatis, nulla, nesciunt eius asperiores. Quidem dolores placeat voluptatum, voluptate temporibus cumque odio facere, dolorum eveniet dolorem in blanditiis nulla enim natus incidunt, doloribus distinctio aut laboriosam ipsum recusandae?</p>
  </div>
</template>

<script>
export default {
  name: 'HomeView',
  components: {}
}
</script>

In AboutView.vue we also add some lorem ipsum text.

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit magnam totam officia expedita quae omnis nobis labore consequuntur maiores dignissimos quas facilis fugiat dolores, iusto in, maxime officiis mollitia distinctio animi nihil accusantium minus voluptates dicta. Assumenda, eaque optio voluptatum porro, incidunt velit aliquam consequatur sequi laboriosam autem praesentium ratione nemo! Totam enim odit, fugit voluptas ipsum tempora dicta cum consectetur omnis illo. Rem consequatur repudiandae possimus velit commodi iusto natus, perspiciatis, nulla, nesciunt eius asperiores. Quidem dolores placeat voluptatum, voluptate temporibus cumque odio facere, dolorum eveniet dolorem in blanditiis nulla enim natus incidunt, doloribus distinctio aut laboriosam ipsum recusandae?</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit magnam totam officia expedita quae omnis nobis labore consequuntur maiores dignissimos quas facilis fugiat dolores, iusto in, maxime officiis mollitia distinctio animi nihil accusantium minus voluptates dicta. Assumenda, eaque optio voluptatum porro, incidunt velit aliquam consequatur sequi laboriosam autem praesentium ratione nemo! Totam enim odit, fugit voluptas ipsum tempora dicta cum consectetur omnis illo. Rem consequatur repudiandae possimus velit commodi iusto natus, perspiciatis, nulla, nesciunt eius asperiores. Quidem dolores placeat voluptatum, voluptate temporibus cumque odio facere, dolorum eveniet dolorem in blanditiis nulla enim natus incidunt, doloribus distinctio aut laboriosam ipsum recusandae?</p>
  </div>
</template>

<script>
export default {
  name: 'AboutView',
  components: {}
}
</script>

48. Folder Structure

In the src directory we have two subdirectories that can hold Vue components: components and views.  The components subdirectory will hold reusable components that are found on various pages.  The views subdirectory will contain components that are specific to a particular page.

We can create subdirectories in both the components and views directories.  For example, if we have a Jobs.vue component that displays a list of jobs, and a JobDetails.vue component that displays the details about a specific job we may want to create a directory src/views/jobs and place both components in it.  Lets do so.

Inside the views directory create a subdirectory named jobs.

Inside src/views/jobs, create a component file named JobsView.vue and add the following content.

Type vue in the file to generate the template.

<template>
  <h1>Jobs</h1>
  <div v-for="job in jobs" :key="job.id">
      <h2> {{ job.title }} </h2>
  </div>
</template>

<script>
export default {
    data() {
        return {
            jobs: [
                {title: 'A', id: 1, details: "aaa"},
                {title: 'B', id: 2, details: "bbb"},
                {title: 'C', id: 3, details: "ccc"}
            ]
        }
    }
}
</script>

<style>

</style>

Now in src/router/index.js, add the route.

import JobsView from '../views/jobs/JobsView.vue' 

const routes = [ 
  ... 
  { path: '/jobs', 
    name: 'jobs', 
    component: JobsView 
  } 
]

And in App.vue add a router-link element to display the JobsView component.

<template>
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link :to="{ name: 'about' }">About</router-link> |
    <router-link :to="{ name: 'jobs' }">Jobs</router-link>
  </nav>
  <router-view/>
</template>

Now, if we want to create a JobDetail component for when a user presses a particular job in the Jobs component we can place the JobDetail.vue file in src/views/jobs along with the JobsView.vue file.

49. Route Parameters

One way to display the job details of a job is to create separate routes for each unique job id.  For example,

  • /jobs/123
  • /jobs/567
  • /jobs/web-dev-621

The path for these routes is defined as

  • /jobs/:id

To set this up, let’s first create a JobDetails.vue file in src/views/jobs.

Next, create a new route in src/router/index.js.

import JobDetails from '../views/jobs/JobDetails.vue'
const routes = [
  ...
  {
    path: '/jobs/:id',
    name: 'job-details',
    component: JobDetails
  }
]

Now when a user enters a url like, …/jobs/123, the JobDetails component will be displayed.  The string 123 will be placed in what is called a router parameter named id.  In the JobDetails component we can access the parameter by using $route.params.id.

<template>
  <h1>Job Details Page</h1>
  <p>The job id is {{ id }}</p>
</template>

<script>
export default {
    data() {
        return {
            id: this.$route.params.id
        }
    }
}
</script>

50. Dynamic Links

To make the job titles on the JobsView component link to the JobDetails component we use router links.  In the to attribute’s object we set the name and params properties.  The names property defines the component name as before.  The params property defines the parameter of the URL.  Recall in the router/index.js we set the route path to ‘/jobs/:id’.  By setting the params property we as specifying the value of the id parameter – namely the id of the particular job.

<template>
  <h1>Jobs</h1>
  <div v-for="job in jobs" :key="job.id">
    <router-link :to="{ name: 'job-details', params: { id: job.id} }" > 
      <h2>{{ job.title }} </h2>
    </router-link>
  </div>
</template>

Now, when a user clicks on a job title in the JobsView component, the URL will change to …/jobs/id where id is the value of the job id.

Now in the JobDetails component we can simplify the code.  If a prop is defined having the same name as a parameter, Vue sets the prop value equal to the parameter value.  So instead of defining the id in the data() property using this.$route.params.id we can simply define a prop named id.

<template>
  <h1 @click="onClick">Job Details Page</h1>
  <p>The job id is {{ id }}</p>
</template>

<script>
export default {
  props: ["id"]
};
</script>

Last, we a little but of style.

In the JobsView component we set the class property in the div to jobs.

<template>
  <h1>Jobs</h1>
  <div v-for="job in jobs" :key="job.id" class="job">
    ...
  </div>
</template>

And we add the following style.

<style>
.job h2 {
  background: #f4f4f4;
  padding: 20px;
  border-radius: 10px;
  margin: 10px auto;
  max-width: 600px;
  cursor: pointer;
  color: #444;
}
.job h2:hover {
  background: #ddd;
}
.job a {
  text-decoration: none;
}
</style>

51. 404 Pages & Redirects

We can redirect a user from an non-existing (perhaps old) route to a particular active routes by adding the old route as a route in router/index.js.  For example, if we used to have a /all-jobs route, but changed it to /jobs, we can add the following to the routes array.

const routes = [
  ...
  {
    path: '/all-jobs',
    redirect: '/jobs'
  }
]

Now we’ll create a 404 page that we’ll display when the user navigates to a page that doesn’t exist.  Create a new component in the views folder named NotFound.vue and add some HTML to display a 404 message.

<template>
  <h2>404</h2>
  <h3>Page not found</h3>
</template>

Now in the router/index.js file we add another route.

import NotFound from '../views/NotFound.vue'

const routes = [
  ...
  {
    path: '/:catchAll(.*)',
    name: 'not-found',
    component: NotFound
  }
]

52. Programmatic Navigation

In this tutorial we show how to access the browser’s history to allow a use to go back and forward, as well as push a route onto the history stack.

We first start by adding 3 buttons to the App.vue template, define the methods in the script tag, and apply some styles to the button.

<button @click="redirect">Redirect</button>
<button @click="back">Back</button>
<button @click="forward">Forward</button>
<script>
export default {
  methods: {
    redirect() {
      this.$router.push({name: 'home'})
    },
    back() {
      this.$router.go(-1)
    },
    forward() {
      this.$router.go(1)
    }
  }
}
</script>
button {
  margin: 0 10px;
  padding: 10px;
  border: none;
  border-radius: 4px;
}

53. Lazy Loading Components

Rather than having all of the components included in the initial Vue bundle, we can exclude some of the components from the initial bundle and load them when the user navigates to a route that uses them.  To do this we provide an import statement in the components property of the route.

For example, to load the about page when a user navigates to the About button we comment out the original import statement, then import it in the route definition.

//import AboutView from '../views/AboutView.vue'

const routes = [
  ...
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  },
  ...
}

 

© 2022, Eric.

Leave a Reply

Your email address will not be published. Required fields are marked *