Setting Up vue-router
Vue Router (vue-router
) is the tool that we will use to route our users within our applications. This is a core piece of the Vue.js framework, and there is a great documentation site available to learn about it. We will cover the fundamental details in this section, but feel free to review the additional documentation.
Getting vue-router
into our applications is easy when using the Vue CLI because it can be included as part of the project skeleton. If we wanted to add vue-router
to an existing project, we could follow the guide provided in the Vue Router documentation.
Bootstrapping a Project with vue-router
When we bootstrap a project, we can choose to use vue-router
. This results in a setup that includes a routes definition and the integration of the routes in several places in the project.
First, in the src/main.js
we can see that the router is included in the imports and setup of the app itself:
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
If we look at the file in src/router/index.js
we can see the route definitions:
import Vue from 'vue'
import Router from 'vue-router'
import FormsPractice from '@/components/FormsPractice'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Forms',
component: FormsPractice
}
]
})
In the App
component, found in src/App.vue
, we can see that the template now includes a <router-view>
element:
<template>
<div id="app">
<h1>Practicing with Forms</h1>
<router-view/>
</div>
</template>
The <router-view>
element is the location in the App template where the router's view will be injected into the application. It replaces the use of the component tag in the template like we saw used in the previous Vue.js projects in this book. This will inject whatever component we have defined in the routes into the HTML at that location. Of course, the next question is: How do we define routes?
Defining Routes
We define routes in the routes/index.js
file as part of the routes
Array. This definition looks something like this:
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import Login from '@/components/Login'
import Register from '@/components/Register'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/register',
name: 'Register',
component: Register
}
]
})
In the example configuration above, we see three different route objects defined: Home
, Login
, and Register
. These three route objects have the same set of properties: path
, name
, and component
. These properties are commonly used, and often the routing for our apps need not get any more complex. Let's take a look at what these properties mean.
The path
property is the desired URL for the route. These URLs should begin with a forward slash (/
), and they can include any URL safe characters. This the URL the user will see in their browser's location bar, and it should make sense based on the content of the view.
The name
property is a unique name for this route in the Vue.js application. The name can be used to reference the route in templates, so instead of linking to /some-view-location
we can link to ViewName
. The advantage here is that we can alter the URLs used in the app by modifying the routes/index.js
file without modifying every template where we link to those routes. This is a very helpful feature of vue-router
and is a common feature among URL routing systems in various frameworks. (We will look more at building navigational linkage in our templates later.)
Finally, the component
property indicates which component should be injected into the <router-view>
element in the application template. This component might utilize several other components as part of its template, and there are even ways to get more complex with loading multiple components in each route, but for most cases this feature works perfectly as it is.
In addition to these fundamentals of route definition, there are a few other more advanced features we often need to use in our route definition.
Dynamic URLs
Sometimes it is necessary to create a URL using data known in the system. A common use case for this is showing a user profile at a URL that looks like this:
http://example.com/user/username
When linking to user profiles in a system, it's necessary to read that username
value out of the URL and process the user profile view accordingly. We can define a route that will give us access to the username
value like this:
const router = new VueRouter({
routes: [
{
path: '/user/:username',
name: 'UserProfile',
component: User
}
]
})
In this case, the :username
portion of the URL defines a "route parameter", which is what we call these variables that are inserted into the URL of a route definition. We use route parameters to modulate the content of the view we show to our users. Route parameters can be referenced in our component logic like this:
<template>
<div class="component">
<h2>{{ welcome }}</h2>
<p>{{ $route.params.username }}</p>
</div>
</template>
<script>
export default {
data () {
return {
salutation: 'Ahoy'
}
},
computed: {
welcome: function () {
return this.salutation + ', ' + this.$route.params.username
}
}
}
</script>
In this example we can see that we are using the $route.params
object in both the component template and logic. In the template, we can directly reference $route.params
to use in creating the interface. In the component logic, we can refer to the route parameters in the same way we refer to other properties defined as part of the component's data
object: this.$route.params
. Any value we assign in the URL of the route definition will be revealed as a property in this.$route.params
.
In the example above, we can see that there is a computed value called welcome
that is being generated. This welcome
value uses this.salutation
and this.$route.params.username
to create a welcome message that is interpolated into our template. If the URL is changed, the page will properly reflect the change in the this.$route.params.username
value.
Note: If we are using more complex components (as we do later in this book) that define a created
function or do other initialization work in the component, we may need to re-initialize the component when the $route.params
object is changed. More information about making that happen can be found on the Dynamic Matching documentation as part of the Vue Router docs.
We could also add multiple values to a route definition. Here is a helpful table showing how route parameters get used in various URLs:
Route Definition | Actual URL | Route Parameters |
---|---|---|
/:username |
/shawnr |
{ 'username': 'shawnr' } |
/:username |
/jdoe |
{ 'username': 'jdoe' } |
/:username/posts/:postid |
/shawnr/posts/12345 |
{ 'username': 'shawnr', 'postid': 12345 } |
/:username/posts/:postid |
/jdoe/posts/9876 |
{ 'username': 'jdoe', 'postid': 9876 } |
Route parameters are powerful tools for coordinating information between different views in our applications, and we will explore many use cases for them throughout this book.
Nested URLs
Sometimes we wish to define URLs as part of a "section" of our site. For example, in many sites we would have some kind of user profile page located at /username
. However, we might also have the following URLs:
/user/username/profile
— User profile page/user/username/profile/edit
— Edit user profile page/user/username/settings
— Site settings page for a specific user/user/username/posts
— Listing of all posts the user has made/user/username/post/12345
— View for a single post made by the user
We see these kinds of related URLs all the time online, and they make sense: We like to group pages with similar functionality under similar URLs. It helps us as developers, and our users, understand our websites and applications better.
The route definition that would create some of these URLs would look like this:
{
path: '/user/:username',
component: User,
children: [
{
// UserProfile will be rendered inside User's <router-view>
// when /user/:username/profile is matched
path: 'profile',
name: 'UserProfile',
component: UserProfile
},
{
// UserPosts will be rendered inside User's <router-view>
// when /user/:username/posts is matched
path: 'posts',
name: 'UserPosts',
component: UserPosts
}
]
}
In this case, we have a general /user/:username
URL that loads the User
component. The User
component will inject an appropriate nested route according to the path
values we have defined. We define nested routes using the children
property on a route definition. This property accepts an array of additional route definitions.
The <router-view>
element in the User
component template looks like this:
<template>
<div class="user">
<h2>{{ welcome }}</h2>
<router-view></router-view>
</div>
</template>
The UserProfile
and UserPosts
components will be rendered inside the <router-view>
tag within the User
template. Each of these components can access the $route.params.username
value in their templates or component logic. Note that the URLs of nested routes defined in the children
property do not use absolute paths—they do not begin with a forward slash ('/'). Rather, they are understood to continue from the path
definition on the parent route.
Moving On
Now that we've got the basics of how routes work, let's move on and discuss creating new components as well as linking our URLs together using <router-link>
tags. There are more features in vue-router
that we have not covered here. Some of those will be touched on later in this book, but it's always worthwhile to visit the official Vue Router documentation to learn more.