Vue Router 路由
单个组件只能做一个页面。真正的应用都有多个页面:首页、用户页、设置页、商品列表……Vue 是"单页应用(SPA)"——所有页面都在同一个 HTML 文件里,通过 JavaScript 切换显示哪个页面。负责"URL → 页面"映射的库就是 Vue Router。本章讲清它的核心用法。
1. SPA 与路由的概念
传统多页网站(MPA):每次点链接,浏览器向服务器请求新 HTML,整页刷新。用户体验差(白屏闪烁)、性能浪费。
单页应用(SPA):整个应用只加载一个 HTML,后续切换页面时不刷新浏览器,由 JS 在前端切换显示的组件。优点是流畅、接近原生 App 体验;但需要一套"URL 与组件的对应规则"——这就是路由。
Vue Router 是 Vue 官方的路由库。当 URL 变化时,它匹配对应的组件,渲染到 <RouterView> 里。同时支持浏览器前进/后退按钮、动态参数、嵌套路由等。
2. 安装
# 用 create-vue 创建项目时勾选 Router 即可
npm create vue@latest
# 或在已有项目里手动安装
npm install vue-router@4用脚手架勾选 Router 是最简单的方式——它会自动帮你装好、生成配置文件、改好 main.js。
3. 基本配置
路由的核心是路由表(routes)——一个数组,每项描述"哪个 URL 对应哪个组件"。
// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
import About from "../views/About.vue";
const routes = [
{ path: "/", name: "home", component: Home },
{ path: "/about", name: "about", component: About },
];
// createWebHistory:HTML5 模式(URL 不带 #)
// createWebHashHistory:Hash 模式(URL 带 #,兼容老服务器)
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;两个历史模式:
- createWebHistory:HTML5 模式,URL 干净(
/about)。需要服务器配置——所有路径都返回 index.html,否则刷新 404。 - createWebHashHistory:Hash 模式,URL 带
#(/#/about)。兼容老服务器,不需要额外配置,但 URL 不太美观。
新项目推荐用 WebHistory 模式,部署时记得配服务器(Vercel/Netlify 自动处理,Nginx 加一行 try_files)。
4. 在 main.js 安装
// src/main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
const app = createApp(App);
app.use(router); // 安装路由插件
app.mount("#app");5. 路由出口与链接
两个核心组件:
<!-- App.vue:路由出口 -->
<template>
<nav>
<!-- router-link 渲染成 a 标签,但走 SPA 跳转 -->
<RouterLink to="/">首页</RouterLink>
<RouterLink to="/about">关于</RouterLink>
<!-- 命名路由 -->
<RouterLink :to="{ name: 'user', params: { id: 1 } }">
用户 1
</RouterLink>
</nav>
<!-- 当前路由匹配的组件渲染在这里 -->
<RouterView />
</template><RouterLink>:渲染成<a>标签,但点击不会真的跳转,而是触发 SPA 内部切换。建议永远用 RouterLink 而不是手写 a 标签(后者会整页刷新)。<RouterView>:路由出口,当前 URL 匹配的组件会渲染在这里。
6. 动态路由参数
URL 里经常带参数:/user/123、/post/2024/06/hello。用冒号声明参数:
// 动态路由:路径里有参数,以冒号开头
const routes = [
// /user/123 → params.id = "123"
{ path: "/user/:id", component: User },
// 多段参数
{ path: "/post/:year/:month/:slug", component: Post },
// 可选参数(加 ?)
{ path: "/search/:keyword?", component: Search },
// 正则匹配
{ path: "/product/:id(\\d+)", component: Product }, // id 必须是数字
];
<!-- User.vue 里读取参数 -->
<script setup>
import { useRoute } from "vue-router";
const route = useRoute();
console.log(route.params.id); // "123"
console.log(route.query.q); // 查询参数 /user/1?q=hi
console.log(route.hash); // 锚点 #section1
</script>组件里用 useRoute() 读取参数。注意 route.params(路径里的)、route.query(?后的查询串)、route.hash(#锚点)的区别。
7. 编程式导航
除了用 <RouterLink>,还可以在 JS 里主动跳转。用 useRouter() 拿到路由实例:
<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
function goAbout() {
// 字符串路径
router.push("/about");
}
function goUser(id) {
// 对象形式(更灵活)
router.push({ path: `/user/${id}` });
router.push({ name: "user", params: { id } });
router.push({ path: "/search", query: { q: "vue" } });
}
function replace() {
// 替换当前历史记录(用户点返回跳不到此页)
router.replace("/login");
}
function goBack() {
router.back(); // 等价 router.go(-1)
router.forward(); // 等价 router.go(1)
}
</script>常见场景:登录成功后跳转、表单提交后跳转、按钮点击跳转。注意 push 会加一条历史记录(可返回),replace 替换当前记录(返回跳不到此页)。
8. 路由懒加载(性能优化)
如果所有页面都打包成一个 JS 文件,首屏会很慢。用动态 import 让每个页面变成独立的 chunk,用户访问时才下载:
// 路由懒加载:用动态 import,Vue 自动代码分割
const routes = [
{ path: "/", component: () => import("../views/Home.vue") },
{ path: "/about", component: () => import("../views/About.vue") },
// 给 chunk 命名(便于打包分析)
{
path: "/user/:id",
component: () => import(/* webpackChunkName: "user" */ "../views/User.vue"),
},
];
// Vite 会自动为每个动态 import 生成独立 chunk
// 用户访问 /about 才下载 About 的代码,首屏更快这是 SPA 性能优化的标配——一个 100 页的应用,用户进首页可能只下载 5 个页面的代码,剩下的按需加载。Vite/Webpack 都会自动处理代码分割。
9. 嵌套路由
实际应用里页面常常有层级:用户中心 → 用户资料 / 用户文章 / 用户设置。Vue Router 用 children 配置嵌套路由:
// 嵌套路由:配置 children
const routes = [
{
path: "/user",
component: UserLayout, // 布局组件
children: [
{ path: "", component: UserHome }, // /user
{ path: "profile", component: UserProfile }, // /user/profile
{ path: "posts", component: UserPosts }, // /user/posts
],
},
];
<!-- UserLayout.vue:父组件里要有 router-view 出口 -->
<template>
<div class="layout">
<h2>用户中心</h2>
<RouterView /> <!-- 子路由渲染在这里 -->
</div>
</template>父组件里也要有 <RouterView> 作为子路由的出口。这种"布局组件"模式非常适合后台管理系统——侧边栏 + 顶部栏在父级,内容区在子路由。
10. 导航守卫(路由拦截)
需求:"用户没登录时不能进 /profile"。Vue Router 提供导航守卫在跳转前后做拦截:
// 全局前置守卫:每次跳转前都会执行
router.beforeEach((to, from) => {
const isLogin = localStorage.getItem("token");
// 想去需要登录的页面,但没登录 → 跳转登录页
if (to.meta.requiresAuth && !isLogin) {
return "/login"; // 返回路径即重定向
}
// 显式返回 false 取消导航
// 不返回 / 返回 true / undefined → 放行
});
// 路由元信息:在路由配置里加 meta 字段
const routes = [
{
path: "/profile",
component: Profile,
meta: { requiresAuth: true }, // 自定义标记
},
];
// 组件内守卫(<script setup>)
import { onBeforeRouteLeave } from "vue-router";
onBeforeRouteLeave((to, from) => {
const answer = window.confirm("确定要离开吗?未保存的内容会丢失");
if (!answer) return false;
});三类守卫:
- 全局守卫:
router.beforeEach/beforeResolve/afterEach。每次跳转都触发。 - 路由独享守卫:在路由配置里写
beforeEnter。只对该路由生效。 - 组件内守卫:
onBeforeRouteEnter/onBeforeRouteUpdate/onBeforeRouteLeave。常用于"未保存提示"。
守卫的返回值决定跳转:true 或不返回 → 放行;false → 取消;路径字符串 → 重定向;Error → 失败。
11. 实战:登录权限控制
把上面的知识组合起来,实现"未登录用户访问任何需要登录的页面都被重定向到登录页":
// router/index.js
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: "/", component: () => import("../views/Home.vue") },
{ path: "/login", component: () => import("../views/Login.vue") },
{
path: "/profile",
component: () => import("../views/Profile.vue"),
meta: { requiresAuth: true }, // 标记需要登录
},
],
});
// 全局守卫:拦截未登录
router.beforeEach((to) => {
const token = localStorage.getItem("token");
if (to.meta.requiresAuth && !token) {
return { path: "/login", query: { redirect: to.fullPath } };
}
});
export default router;这种"meta 标记 + 守卫检查"的模式是 SPA 权限控制的标准方案。比在每个组件里检查登录优雅得多。
小结
这一章你掌握了 Vue Router 的核心:路由表配置、动态参数、嵌套路由、懒加载、导航守卫。能做出多页面、带权限控制的 SPA 应用。最后一章我们看状态管理——当多个组件需要共享数据时,Pinia 是现代 Vue 的标准答案。
← 上一篇 Vue 生命周期
下一篇 Vue 状态管理 Pinia Vuex →