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;

两个历史模式:

新项目推荐用 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>

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;
});

三类守卫:

守卫的返回值决定跳转: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

✈️💬