Route Navigation
In this chapter, you will learn to use Vue Router to implement page navigation with front-end/back-end separation, including dynamic route parameters.
Why use routing?
Traditional multi-page websites request a new HTML file from the server when you click a link, causing the entire page to refresh with a white-screen flicker.
What Vue Router implements isfront-end routing: When the URL changes, no request is sent to the server; instead, JS dynamically switches the displayed component.
Benefits of this approach:Page switching without refresh, providing a smooth experience like a native app。
Vue Router Core Concepts
| Concept | Purpose | Corresponding Code |
|---|---|---|
| Route Table | Defines the mapping between URL paths and components | routesArray |
| Router Outlet | The location where the matched component is displayed | <RouterView /> |
| Navigation Link | Click to navigate to the specified route | <RouterLink to="..."> |
| Dynamic Routing | URL contains variables (e.g., article ID) | /post/:id |
Installation and Configuration
If you already selected Router when creating the project, you can skip the installation step.
If you didn't select it, run this in the terminal:
$ npm install vue-router@4
Create the route configuration file
Example
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// Route table: each record maps a path to a component
const routes = [
{
path: '/', // URL path
name: 'home', // Route name (optional, used in programmatic navigation)
component: HomeView // Corresponding page component
},
{
// Dynamic route: :id is a placeholder that matches any value
// /post/1, /post/2, /post/hello will all match this entry
path: '/post/:id',
name: 'post',
// Lazy loading: loaded only when accessed; bundling splits out a separate JS file
component: () => import('../views/PostView.vue')
}
]
// Create the router instance
const router = createRouter({
history: createWebHistory(), // Use HTML5 History mode (URL looks like a normal path)
routes // Register the route table
})
export default router
Differences between the two router modes:createWebHistoryProduces clean URLs (/post/1), requires server configuration support. If the deployment environment is uncertain, you can usecreateWebHashHistory, where the URL contains a # symbol (/#/post/1), no server configuration needed.
Register the router in main.js
Example
<script setup>
import NavBar from './components/NavBar.vue'
</script>
<template>
<NavBar />
<main>
<!-- RouterView is the magic spot: the component matched by the current route will be rendered here -->
<RouterView />
</main>
</template>
RouterViewis the core outlet of the routing system.
When the URL is/, it renders HomeView; when the URL is/post/1, it renders PostView.
RouterLink — Declarative Navigation
RouterLinkis a navigation component provided by Vue Router, replacing the traditional<a>tag.
It does not trigger a page refresh; it only switches the displayed component via JS.
Example
<!-- Normal syntax -->
<RouterLink to="/">Home</RouterLink>
<!-- Named route syntax (recommended: only one place to change when path changes) -->
<RouterLink :to="{ name: 'post', params: { id: article.id } }">
Read article {{ article.id }}
</RouterLink>
<!-- Traditional a tag causes page refresh, do not use -->
<!-- <a href="/post/1">Read full article</a> -->
</template>
useRoute — Reading Route Parameters
In the detail page PostView, you need to know the article ID in the URL to find the corresponding article.
useRouteis used to read the current route information.
Example
<script setup>
import { useRoute } from 'vue-router'
// useRoute returns the current route object
const route = useRoute()
// route.params contains the parameters in the URL
// For example, if the URL is /post/3, then route.params.id === '3'
console.log('Article ID:', route.params.id)
</script>
<template>
<div>
<h1>Article Detail Page</h1>
<!-- Note: values in params are all strings -->
<p>Current article ID: {{ route.params.id }}</p>
</div>
</template>
route.params.idvalue is always a string. Even if the URL is/post/3, the retrieved value is'3'not a number3. When a number is needed, useNumber(route.params.id)orparseIntto convert.
Hands-on: Complete Article Detail Page
Make BlogCard clickable for navigation, and the detail page displays the full article content based on the ID.
Step 1: Modify BlogCard to support click navigation
Example
<script setup>
defineProps({
id: Number, // New: article ID for navigation
title: String,
summary: String,
date: String,
category: String
})
</script>
<template>
<!-- The whole card is wrapped in RouterLink, so clicking anywhere navigates -->
<RouterLink :to="{ name: 'post', params: { id } }" class="card-link">
<div class="card">
<span class="tag">{{ category }}</span>
<h3>{{ title }}</h3>
<p>{{ summary }}</p>
<span class="date">{{ date }}</span>
</div>
</RouterLink>
</template>
<style scoped>
.card-link { text-decoration: none; color: inherit; display: block; }
.card {
background: #fff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.tag {
display: inline-block;
padding: 2px 10px;
background: #E8EEF5;
color: #427DB8;
border-radius: 12px;
font-size: 12px;
}
.card h3 { margin: 10px 0 8px; font-size: 18px; }
.card p { color: #666; font-size: 14px; line-height: 1.6; }
.date { font-size: 12px; color: #999; }
</style>
Step 2: Create the PostView detail page
The detail page reads the ID from the URL, then finds and displays the corresponding article from the article data.
Example
<script setup>
import { ref, computed } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
const route = useRoute()
const id = Number(route.params.id)
// All article data (will be extracted to a shared module in later chapters)
const articles = ref([
{
id: 1,
title: 'The Complete Guide to Getting Started with Vue3',
summary: 'Learn Vue3 Composition API from scratch',
category: 'Vue',
date: '2024-05-10',
content: `
<h2>Why learn Vue3?</h2>
<p>Vue3 is one of the most popular front-end frameworks today; it is easy to learn, flexible, and performs excellently.</p>
<h2>Advantages of the Composition API</h2>
<p>Compared to Vue2's Options API, the Composition API makes logic reuse simple and code organization more flexible.</p>
<h2>ref and reactive</h2>
<p>ref is suitable for primitive type data, while reactive is suitable for objects and arrays. However, in real development it is recommended to consistently use ref.</p>
`
},
{
id: 2,
title: 'Detailed Explanation of JS Async Programming',
summary: 'Understand Promise and async/await',
category: 'JavaScript',
date: '2024-05-08',
content: `
<h2>What is asynchronous?</h2>
<p>JS is single-threaded; asynchronous operations allow the main thread to not block while handling tasks such as network requests and timers.</p>
<h2>Core Concepts of Promise</h2>
<p>Promise has three states: pending (in progress), fulfilled (succeeded), rejected (failed).</p>
`
}
])
// Find the article by ID
const article = computed(() => {
return articles.value.find(a => a.id === id)
})
</script>
<template>
<div class="post-view">
<!-- Article not found -->
<div v-if="!article" class="not-found">
<h2>Article Not Found</h2>
<p>Article with ID {{ id }} not found</p>
<RouterLink to="/">Back to Home</RouterLink>
</div>
<!-- Article exists, display full content -->
<article v-else>
<span class="category-tag">{{ article.category }}</span>
<h1>{{ article.title }}</h1>
<time>{{ article.date }}</time>
<!-- v-html renders HTML strings -->
<div class="content" v-html="article.content"></div>
<RouterLink to="/" class="back-link">← Back to Home</RouterLink>
</article>
</div>
</template>
<style scoped>
.post-view {
max-width: 720px;
margin: 40px auto;
padding: 0 20px;
}
.category-tag {
display: inline-block;
padding: 4px 12px;
background: #E8EEF5;
color: #427DB8;
border-radius: 12px;
font-size: 13px;
margin-bottom: 12px;
}
h1 {
font-size: 32px;
margin-bottom: 12px;
line-height: 1.4;
}
time {
display: block;
color: #999;
font-size: 14px;
margin-bottom: 30px;
}
.content {
line-height: 1.8;
font-size: 16px;
color: #333;
}
.content :deep(h2) {
margin: 24px 0 12px;
font-size: 22px;
}
.content :deep(p) {
margin-bottom: 12px;
}
.back-link {
display: inline-block;
margin-top: 40px;
color: #427DB8;
text-decoration: none;
}
.not-found {
text-align: center;
padding: 60px 0;
}
</style>
v-htmlis used to render HTML strings, but note: do not use it to render user input, otherwise there is an XSS security risk. The article content here is written by developers themselves, so it is safe.
Chapter Summary
In this chapter, you learned the core usage of Vue Router: route table configuration (path maps to component), RouterLink navigation, RouterView outlet, dynamic routes /post/:id, and useRoute to read route parameters.
Now the blog has a true multi-page experience — browse the list on the homepage, click to enter the detail page to read the full content.
Other extensions