路由跳转
本章你将学会用 Vue Router 实现前后端分离的页面跳转,包括动态路由参数。
为什么要用路由?
传统的多页面网站,点击链接会向服务器请求新的 HTML 文件,页面整个刷新,有白屏闪烁。
Vue Router 实现的是 前端路由:URL 变化时不向服务器发请求,而是由 JS 动态切换显示的组件。
这样做的好处:无刷新切换页面,体验像原生 APP 一样流畅。
Vue Router 核心概念
| 概念 | 作用 | 对应代码 |
|---|---|---|
| 路由表 | 定义 URL 路径与组件的映射关系 | routes 数组 |
| 路由出口 | 匹配到的组件显示的位置 | <RouterView /> |
| 导航链接 | 点击跳转到指定路由 | <RouterLink to="..."> |
| 动态路由 | URL 中包含变量(如文章 ID) | /post/:id |
安装与配置
如果你在创建项目时已选择 Router,可以跳过安装步骤。
如果没选,终端执行:
$ npm install vue-router@4
创建路由配置文件
实例
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 路由表:每条记录映射一个路径到一个组件
const routes = [
{
path: '/', // URL 路径
name: 'home', // 路由名称(可选,编程式导航时用)
component: HomeView // 对应的页面组件
},
{
// 动态路由::id 是占位符,匹配任意值
// /post/1、/post/2、/post/hello 都会匹配到这条
path: '/post/:id',
name: 'post',
// 懒加载:访问时才加载,打包时会拆分出独立的 JS 文件
component: () => import('../views/PostView.vue')
}
]
// 创建路由实例
const router = createRouter({
history: createWebHistory(), // 使用 HTML5 History 模式(URL 看起来像普通路径)
routes // 注册路由表
})
export default router
两种路由模式的区别:createWebHistory 产生干净的 URL(/post/1),需要服务器配置支持。如果部署环境不确定,可以用 createWebHashHistory,URL 中带 # 号(/#/post/1),无需服务器配置。
在 main.js 中注册路由
实例
<script setup>
import NavBar from './components/NavBar.vue'
</script>
<template>
<NavBar />
<main>
<!-- RouterView 是魔法位置:当前路由匹配到的组件会在这里渲染 -->
<RouterView />
</main>
</template>
RouterView 是路由系统的核心出口。
当 URL 为 / 时,它会渲染 HomeView;当 URL 为 /post/1 时,它会渲染 PostView。
RouterLink — 声明式导航
RouterLink 是 Vue Router 提供的导航组件,替代了传统的 <a> 标签。
它不会触发页面刷新,只会通过 JS 切换显示的组件。
实例
<!-- 普通写法 -->
<RouterLink to="/">首页</RouterLink>
<!-- 命名路由写法(更推荐:路径变了也只改一处) -->
<RouterLink :to="{ name: 'post', params: { id: article.id } }">
阅读文章 {{ article.id }}
</RouterLink>
<!-- 传统 a 标签会导致页面刷新,不要用 -->
<!-- <a href="/post/1">阅读全文</a> -->
</template>
useRoute — 读取路由参数
在详情页 PostView 中,需要知道 URL 中的文章 ID 是多少,才能找到对应的文章。
useRoute 就是用来读取当前路由信息的。
实例
<script setup>
import { useRoute } from 'vue-router'
// useRoute 返回当前路由对象
const route = useRoute()
// route.params 包含 URL 中的参数
// 例如 URL 为 /post/3,则 route.params.id === '3'
console.log('文章 ID:', route.params.id)
</script>
<template>
<div>
<h1>文章详情页</h1>
<!-- 注意:params 中的值都是字符串 -->
<p>当前文章 ID:{{ route.params.id }}</p>
</div>
</template>
route.params.id的值始终是字符串。即使 URL 是/post/3,取到的也是'3'而非数字3。需要数字时,用Number(route.params.id)或parseInt转换。
动手:完整的文章详情页
把 BlogCard 改成可点击跳转,详情页根据 ID 显示完整文章内容。
第一步:改造 BlogCard 支持点击跳转
实例
<script setup>
defineProps({
id: Number, // 新增:文章 ID,用于跳转
title: String,
summary: String,
date: String,
category: String
})
</script>
<template>
<!-- 整个卡片被 RouterLink 包裹,点击任意位置都能跳转 -->
<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: #e8f5e9;
color: #42b883;
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>
第二步:创建 PostView 详情页
详情页从 URL 中读取 ID,然后从文章数据中找到对应的文章显示。
实例
<script setup>
import { ref, computed } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
const route = useRoute()
const id = Number(route.params.id)
// 所有文章数据(后续章节会抽离到公共模块)
const articles = ref([
{
id: 1,
title: 'Vue3 入门完全指南',
summary: '从零开始学习 Vue3 组合式 API',
category: 'Vue',
date: '2024-05-10',
content: `
<h2>为什么学 Vue3?</h2>
<p>Vue3 是目前最流行的前端框架之一,它易学、灵活、性能出色。</p>
<h2>组合式 API 的优势</h2>
<p>相比 Vue2 的选项式 API,组合式 API 让逻辑复用变得简单,代码组织更加灵活。</p>
<h2>ref 与 reactive</h2>
<p>ref 适合基本类型数据,reactive 适合对象和数组。但实际开发中推荐统一使用 ref。</p>
`
},
{
id: 2,
title: 'JS 异步编程详解',
summary: '搞懂 Promise 和 async/await',
category: 'JavaScript',
date: '2024-05-08',
content: `
<h2>什么是异步?</h2>
<p>JS 是单线程的,异步操作可以让主线程不阻塞,同时处理网络请求、定时器等任务。</p>
<h2>Promise 的核心概念</h2>
<p>Promise 有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。</p>
`
}
])
// 根据 ID 查找文章
const article = computed(() => {
return articles.value.find(a => a.id === id)
})
</script>
<template>
<div class="post-view">
<!-- 文章不存在 -->
<div v-if="!article" class="not-found">
<h2>文章不存在</h2>
<p>找不到 ID 为 {{ id }} 的文章</p>
<RouterLink to="/">返回首页</RouterLink>
</div>
<!-- 文章存在,展示完整内容 -->
<article v-else>
<span class="category-tag">{{ article.category }}</span>
<h1>{{ article.title }}</h1>
<time>{{ article.date }}</time>
<!-- v-html 渲染 HTML 字符串 -->
<div class="content" v-html="article.content"></div>
<RouterLink to="/" class="back-link">← 返回首页</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: #e8f5e9;
color: #42b883;
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: #42b883;
text-decoration: none;
}
.not-found {
text-align: center;
padding: 60px 0;
}
</style>
v-html用于渲染 HTML 字符串,但要注意:不要用它渲染用户输入的内容,否则有 XSS 安全风险。这里的文章内容是开发者自己写的,所以是安全的。
本章小结
本章你学会了 Vue Router 的核心使用:路由表配置(path 映射到 component)、RouterLink 导航、RouterView 出口、动态路由 /post/:id、以及 useRoute 读取路由参数。
现在博客有了真正的多页面体验——首页浏览列表,点击进入详情页阅读完整内容。
