A single page application is just that; a single page. Navigating between “pages” is not really possible in the traditional sense; you cannot just link to another page. Instead, you are linking to a route, which then loads (or mounts) a set of components that you define and renders them to the view in your browser.
In order to achieve this, a JavaScript library is needed to load the components that you defined per that route. The Vue Core Team has created an official routing library called, “Vue Router” that is automatically installed (if selected during setup) when working on a Vue CLI project. Other frameworks have their “own” libraries, but what makes Vue Router so special is that it is indeed developed and maintained by the Core Team. When Vue gets updated, there’s a good chance that Vue Router will as well. This means that Vue Router is guaranteed to work with all versions new or old of Vue.js.
React.js, for example, does not have an official routing library. There are of course “standard” or widely recommended libraries (like React Router 4) but none that are developed by the React team at Facebook. This is also the same for state management; Vue’s state management library is created and maintained by the Core Team. However, since React Router 4 is independent of React, there isn’t a guarantee that the two libraries will always work together.
You don’t need to create a Webpack-enabled Vue.js application to use Vue Router. You can use Vue Router alongside the Vue.js library in a static HTML page. Just like the core Vue.js library, you can also install Vue Router with just a CDN.
<
script
src
=
"https://unpkg.com/vue-router/dist/vue-router.js"
><
/script>
Note: The Vue Router library, as well as all other companion libraries, should be loaded after Vue.js.
index.html
<html>
<head>
<title>
A static Vue.js Application with Vue Router</title>
<!-- Vue.js -->
<script
src=
"https://unpkg.com/vue/dist/vue.js"
></script>
<!-- Vue Router -->
<script
src=
"https://unpkg.com/vue-router/dist/vue-router.js"
></script>
<script
src=
"js/app.js"
></script>
</head>
<body>
<div
id=
"app"
>
<h1>
My App</h1>
<ul>
<li><router-link
to=
"/page-one"
>
Go to Page One</router-link><li>
<li><router-link
to=
"/page-two"
>
Go to Page Two</router-link></li>
</ul>
<router-view></router-view>
</div>
</body>
</html>
The <router-link>
component renders an anchor tag (<a href="#">
) that links to that route. The <router-view />
component loads the component(s) or template(s) that you defined in your external JavaScript page.
js/app.js
const
PageOne
=
{
template
:
`
<div>
<p>I am the Page One component.</p>
</div>
`
,
};
const
PageTwo
=
{
template
:
`
<div>
<p>I am the Page Two component.</p>
</div>
`
,
};
const
routes
=
[
{
path
:
'/page-one'
,
component
:
PageOne
},
{
path
:
'/page-two'
,
component
:
PageTwo
}
];
const
router
=
new
VueRouter
({
routes
,
// short for `routes: routes`
});
const
app
=
new
Vue
({
router
,
stat
,
}).
$mount
(
'#app'
);
Note: The JavaScript code is written in ES6. You will need to add a compiler like Babel if you want to run this in the browser. However, you can easily convert this ES6 code to the more supported ES5 syntax, which doesn’t require Babel.
Vue Router is most useful when working with a Webpack, Node.js, or a module-based single page application systems like the apps created with Vue CLI 3. Chapter 2: Scaffolding Projects With Vue CLI 3 goes over creating a Webpack built Vue.js application with Vue CLI 3. If you haven’t read Chapter 2 yet, it’s recommended that you read it so you have a general understanding of single file components and Webpack.
If you generated a project with Vue CLI 3 or the Vue CLI UI, there is an option to include Vue Router during the setup process. If you did not select Vue Router during the setup process, you can still import it via NPM or Yarn and import
them into your project with ECMAScript6 (ES6).
$
npm install vue-router --save# or
$
yarn add vue-router
In the src
directory of your application, create a router.js
file. This file will store all of your application’s routes. In this file, you can define which component gets mounted when a certain route is visited. For now, leave this file blank. Let’s add it to the main Vue Instance in the main.js
file.
In your main.js
file, you should see something similar to the snippet below.
main.js
import
Vue
from
'vue'
;
import
App
from
'./App.vue'
;
import
'./registerServiceWorker'
;
Vue
.
config
.
productionTip
=
false
;
new
Vue
({
render
:
h
=>
h
(
App
),
}).
$mount
(
'#app'
);
This is the bare bones Vue Instance of the application. Go ahead and import
your router file and add it as a dependency in your instance.
main.js
import
Vue
from
'vue'
;
import
App
from
'./App.vue'
;
import
router
from
'./router'
;
import
'./registerServiceWorker'
;
Vue
.
config
.
productionTip
=
false
;
new
Vue
({
router
,
// short for router: router
render
:
h
=>
h
(
App
),
}).
$mount
(
'#app'
);
Save this file. Your router is now part of the instance and you can now create routes globally in the application. Before you start adding routes, you need to add the <router-view />
component to the App.vue
file. This is where the component (per route) gets mounted and injected into.
App.vue
<template>
<div
id=
"app"
>
<!-- el in the main Vue Instance -->
<router-view/>
<!-- components per routes get mounted here -->
</div>
</template>
Now that your router is set up, let’s flesh out the router.js
file. The first thing that you will need to do is import Vue and the vue-router
library.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
Next, we want to tell Vue to use the vue-router
library.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
Home
from
'./views/Home.vue'
;
Vue
.
use
(
Router
);
Next, let’s export the Router
object with a routes
property.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
Home
from
'./views/Home.vue'
;
Vue
.
use
(
Router
);
export
default
new
Router
({
routes
:
[],
});
This routes
object is where you will add objects that define the route. The properties that the route object takes are path
: the URL path itself, name
: the name of the route (more of this later), and component
: The actual component that gets mounted with the path
is visited in the URL bar.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
Home
from
'./views/Home.vue'
;
Vue
.
use
(
Router
);
export
default
new
Router
({
routes
:
[
{
path
:
'/'
,
name
:
'home'
,
component
:
Home
,
},
],
});
The route object above is loading the Home
component when the root route is accessed (ex: localhost:3000/
). To add more routes, you will need to import
the route and create another object in the routes
array.
In a previous section, you linked another route via plan ES6 JavaScript with the <router-link />
component. The <router-link />
component accepts a single prop: to
. The to
prop is equivalent to href
with the <a>
HTML tag. In fact, <router-link />
actually gets rendered as an anchor tag with a href
attribute.
The easiest way to link to another route is to pass in a string to the to
prop. The string directory corresponds to the path
property in the route
object in the router.js
<router-link
to=
"/about"
>
To the About Page</router-link>
The router-link
above renders out to a <a>
tag:
<a
href=
"/about"
>
To the About Page</a>
Sometimes it’s easier to remember a name of a route versus the string URL path. You can link to another route by using the name
property in the route object.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
AboutMe
from
'./views/About.vue'
;
Vue
.
use
(
Router
);
export
default
new
Router
({
routes
:
[
{
path
:
'/'
,
name
:
'about'
,
component
:
AboutMe
,
},
],
});
To link to a route using the name, pass in an object into the to
prop:
<router-link
:to=
"{ name: 'about'}"
>
To the About Me Page</router-link>
You can also pass in parameters into your route if you have dynamic routes or a page with different data.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
SingleProject
from
'./views/SingleProject.vue'
;
Vue
.
use
(
Router
);
export
default
new
Router
({
routes
:
[
{
path
:
'/work/:projectId'
,
name
:
'singleProject'
,
component
:
SingleProject
,
},
],
});
<router-link
:to=
"{ name: 'singleProject', params: { projectId: 'some-project' }}"
>
To a Single Project</router-link>
A single route can also have many components associated with it. This is useful if you want to add another <router-view />
to a specific route, like the main view and a sidebar, for example.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
Main
from
'./views/Main.vue'
;
import
Sidebar
from
'./views/Sidebar.vue'
;
Vue
.
use
(
Router
);
export
default
new
Router
({
routes
:
[
{
path
:
'/'
,
components
:
{
default
:
Main
,
sidebar
:
Sidebar
,
},
},
],
});
In the snippet above, you are importing the Main
and Sidebar
components and assigning them to a name
. In this case, the default component for the /
route is Main
. When we link to a route with multiple components, it’ll mount both components as one.
<div
id=
"app"
>
<router-view/>
<!-- mount the default component, which is "Main" -->
<router-view
name=
"sidebar"
/>
<!-- mounts the sidebar -->
</div>
Let’s talk about web applications like GitHub and Twitter for a second. Notice anything about them? Well, it depends whether or not you are logged into those services. If you are not logged in, you will see the home page with a login form. If you are logged in, you will see your dashboard or timeline.
Granted, GitHub and Twitter are not using Vue.js (yet...) but this type of functionality can be achieved with route aliases. To add an alias, just add the alias
property to a route object in your router.js
.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
Login
from
'./views/Login.vue'
;
import
Dashboard
from
'./views/Dashboard.vue'
;
Vue
.
use
(
Router
);
export
default
new
Router
({
routes
:
[
{
path
:
'/'
,
component
:
Login
,
name
:
'login'
,
},
{
path
:
'/dashboard'
,
component
:
Dashboard
,
name
:
'dashboard'
,
alias
:
'/'
,
// Alias right here
},
],
});
With the alias above, you are telling Vue.js to load the /dashboard
route and it’s components but keep the URL as /'. In your code, you will add a
router-linkbut link it to the
/dashboard` route; Vue Router will handle the rest.
<router-link
to=
"/dashboard"
>
To Dashboard</router-link>
<!-- Link to the dashboard route, load it's components but keep the URL as `/` -->
Similar to alias, redirects will redirect the linked route to another route when visited.
router.js
export
default
new
Router
({
routes
:
[
{
path
:
'/'
,
redirect
:
'
/
dashboard
},
],
});
<router-link
to=
"/"
>
To Dashboard</router-link>
<!-- Link to the main route, but redirect to 'dashboard` -->
If you are familiar with Apache rewrite rules, you can kind of think of them that way. They’re just handled within your application using Vue Router.
Currently, in your application, the URLs are prefixed with /#/
this is the default mode which is also known as “hash mode”. When in hash mode, Vue.js uses the /#/
to simulate a URL so the page won’t be reloaded when the URL changes. However, you can change this by enabling history mode. In history mode, Vue Router takes advantage of the ‘history.pushState` API to prevent the page from reloading.
To enable history mode, just add the mode
attribute with a value of history
to the VueRouter
object.
router.js
export
default
new
Router
({
mode
:
'history'
,
// Down here!
routes
:
[
{
path
:
'/'
,
name
:
'home'
,
component
:
Home
,
},
],
});
This removes the hash in the URL and makes the URL path look “normal” as desired. However, since it’s a singe page application, in history mode and without server configurations, navigating to a route will result in a 404 page. To fix this you can add the server configurations provided in the official Vue Router documentation.
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index.
html$
-[
L]
RewriteCond %{
REQUEST_FILENAME}
!-f RewriteCond %{
REQUEST_FILENAME}
!-d RewriteRule . /index.html[
L]
</IfModule>
location /{
try_files$uri
$uri
/ /index.html;
}
const
http
=
require
(
'http'
);
const
fs
=
require
(
'fs'
);
const
httpPort
=
80
;
http
.
createServer
((
req
,
res
)
=>
{
fs
.
readFile
(
'index.htm'
,
'utf-8'
,
(
err
,
content
)
=>
{
if
(
err
)
{
console
.
log
(
'We cannot open "index.htm" file.'
);
}
res
.
writeHead
(
200
,
{
'Content-Type'
:
'text/html; charset=utf-8'
,
});
res
.
end
(
content
);
});
})
.
listen
(
httpPort
,
()
=>
{
console
.
log
(
'Server listening on: http://localhost:%s'
,
httpPort
);
});
web.config
file in the root directory of your site with the following:<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule
name=
"Handle History Mode and custom 404/500"
stopProcessing=
"true"
>
<match
url=
"(.*)"
/>
<conditions
logicalGrouping=
"MatchAll"
>
<add
input=
"{REQUEST_FILENAME}"
matchType=
"IsFile"
negate=
"true"
/>
<add
input=
"{REQUEST_FILENAME}"
matchType=
"IsDirectory"
negate=
"true"
/>
</conditions>
<action
type=
"Rewrite"
url=
"/"
/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
rewrite{
regexp .* to{
path}
/}
Add the following to your firebase.json
:
{
"hosting"
:
{
"public"
:
"dist"
,
"rewrites"
:
[
{
"source"
:
"**"
,
"destination"
:
"/index.html"
}
]
}
}
There is one caveat to this though: Your server will no longer report 404 errors since all of your paths now redirect to the index
route. You can get around this by creating a 404 component that displays if any route does not route. Create another route object with a wildcard (*
) and include the 404 component to mount if a component is not found.
router.js
import
Vue
from
'vue'
;
import
Router
from
'vue-router'
;
import
Home
from
'./views/Home'
;
import
NotFound
from
'./views/NotFound'
;
Vue
.
use
(
Router
);
export
default
new
Router
({
mode
:
'history'
,
routes
:
[
{
path
:
'/'
,
name
:
'home'
,
component
:
Home
,
},
{
path
:
'*'
,
component
:
NotFound
,
},
],
});
Vue Router is a great library to navigate between the different views or “pages” of your application. All of your routes live inside one router.js
file that gets injected as a dependency into your Vue Instance. As stated above, there are different types of routes that you can have including named routes, dynamic routes, redirects, alias, and stringed routes.
Unlike its competitors, Vue Router is a first party proprietary router created by the Vue.js Core Team. Since it’s the first party router for Vue.js, Vue Router is guaranteed to work with the latest versions of the core Vue.js library. With React, for instance, the unofficial “official” recommendation is React Router 4, which could change at any time and has a higher chance of introducing breaking changes to your application. With that being said, you do not need to use Vue Router in your application. You can use a third party Vue.js router. However, it is not recommended.
The Vue Router documentation (as well as all of the other docs) is written and maintained by Chris Fritz and Sarah Drasner.
3.141.100.120