Skip to content

Vue 路由

一个页面装不下所有功能——文章列表、文章详情、发布表单、登录页,这些是不同的"页面"。在传统网站里每点一个链接就刷新整个页面、服务端返回新 HTML。Vue 做的是单页应用(SPA):页面不刷新,JS 在前端切换显示的内容。管理这种"页面切换"的工具叫路由

一、安装 Vue Router

Vue Router 是 Vue 官方的路由库,需要在项目里额外安装:

bash
npm install vue-router@4

二、定义路由

路由就是一张"地址 → 组件"的对照表。访问 /articles 时显示文章列表组件,访问 /articles/1 时显示详情组件:

text
src/
├── router/
│   └── index.js        # 路由配置
├── views/              # 页面级组件放这
│   ├── ArticleList.vue
│   ├── ArticleDetail.vue
│   ├── ArticleForm.vue
│   └── Login.vue
└── main.js             # 入口,挂载路由

路由配置 src/router/index.js

js
import { createRouter, createWebHistory } from "vue-router";
import ArticleList from "../views/ArticleList.vue";
import ArticleDetail from "../views/ArticleDetail.vue";
import ArticleForm from "../views/ArticleForm.vue";
import Login from "../views/Login.vue";

const routes = [
    { path: "/", redirect: "/articles" },              // 首页重定向到文章列表
    { path: "/articles", component: ArticleList },      // 文章列表
    { path: "/articles/:id", component: ArticleDetail }, // 文章详情(:id 是动态参数)
    { path: "/articles/new", component: ArticleForm },   // 新建文章
    { path: "/login", component: Login },                // 登录
];

const router = createRouter({
    history: createWebHistory(),  // 用 HTML5 History 模式,URL 里没有 #
    routes,
});

export default router;

每条路由是一个对象:path 是 URL 路径,component 是访问这个路径时显示的组件。/articles/:id 里的 :id动态参数——/articles/1/articles/2 都匹配这条路由,id 分别是 12

入口 src/main.js 里挂载路由:

js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";

createApp(App).use(router).mount("#app");

三、路由出口和导航

<router-view> 是路由出口——当前路由匹配到的组件会显示在这个位置<router-link> 是路由导航链接,点击后切换路由不刷新页面:

vue
<!-- App.vue -->
<template>
    <nav>
        <router-link to="/articles">文章列表</router-link>
        <router-link to="/articles/new">写文章</router-link>
    </nav>

    <!-- 当前路由的组件显示在这 -->
    <router-view />
</template>

<router-link to="/articles"> 渲染成一个 <a> 标签,点击后 URL 变成 /articles<router-view> 里显示 ArticleList 组件。整个过程不刷新页面,所以不会闪一下白屏。

四、读取路由参数

详情页要拿到 URL 里的 id,用 useRoute

vue
<!-- views/ArticleDetail.vue -->
<template>
    <div v-if="loading">加载中...</div>
    <div v-else-if="article">
        <h2>{{ article.title }}</h2>
        <p>{{ article.content }}</p>
    </div>
    <button @click="goBack">返回列表</button>
</template>

<script setup>
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";

const route = useRoute();   // 读取当前路由信息
const router = useRouter(); // 编程式导航(跳页面)

const article = ref(null);
const loading = ref(true);

onMounted(async () => {
    // route.params.id 拿到 URL 里的 :id 参数
    const id = route.params.id;
    console.log("加载文章 ID:", id);

    // 实际场景:调后端接口
    // article.value = await request(`/articles/${id}`);

    // 模拟数据
    article.value = { title: `文章 ${id}`, content: "这是正文内容。" };
    loading.value = false;
});

function goBack() {
    router.push("/articles");  // 编程式跳转,等价于点击 router-link to="/articles"
}
</script>

useRoute() 返回当前路由信息,route.params.id 就是 URL 里 /articles/:idid 部分。useRouter() 返回路由器实例,router.push()编程式导航——在代码里跳页面,不用用户点链接。

编程式导航的几种写法

js
const router = useRouter();

// 字符串路径
router.push("/articles");

// 对象写法,可以带参数
router.push({ path: `/articles/${id}` });

// 命名路由(路由配置里加了 name 时才能用)
router.push({ name: "articleDetail", params: { id: 1 } });

// 替换当前页面(不留历史记录,用户点后退回不到当前页)
router.replace("/login");

// 前进后退
router.back();   // 等价于浏览器的后退
router.forward(); // 等价于浏览器的前进

五、导航守卫——登录拦截

有些页面需要登录才能访问。导航守卫在路由跳转前检查条件,不满足就拦截到登录页:

js
// router/index.js 末尾加
router.beforeEach((to, from) => {
    const token = localStorage.getItem("access_token");

    // 没登录且访问的不是登录页 → 跳到登录页
    if (!token && to.path !== "/login") {
        return "/login";
    }

    // 已登录还想去登录页 → 跳回首页
    if (token && to.path === "/login") {
        return "/articles";
    }

    // 其他情况正常放行(不 return 或 return true)
});

beforeEach 在每次路由跳转前执行,参数是 to(目标路由)和 from(来源路由)。返回一个路径字符串表示重定向——返回 "/login" 就跳到登录页。不返回或返回 true 表示放行。

这是前端权限控制的基础——没 Token 的用户访问需要登录的页面,自动跳到登录页。注意这只是前端层面的检查,真正的权限验证在后端——前端拦住了体验更好,但后端接口也必须校验 Token,不能只靠前端。

六、完整页面结构

把这些合起来,一个带路由的文章管理应用结构大概是这样:

text
src/
├── router/
│   └── index.js         # 路由配置 + 导航守卫
├── api/
│   └── client.js        # fetch 封装(06 篇写的那个)
├── views/
│   ├── Login.vue        # 登录页
│   ├── ArticleList.vue  # 文章列表
│   ├── ArticleDetail.vue # 文章详情
│   └── ArticleForm.vue  # 新建/编辑文章
├── components/
│   └── ArticleCard.vue  # 可复用的文章卡片组件
├── App.vue              # 根组件(导航栏 + router-view)
└── main.js              # 入口
  • views/ 放页面级组件(跟路由一一对应)
  • components/ 放可复用的小组件(多处用的卡片、按钮、表单片段)
  • api/ 放 fetch 封装和接口函数
  • router/ 放路由配置和导航守卫

这个结构在后端基础篇的项目实战里会直接用——运维平台的前端就是同样的 views + components + api + router 四层,只是页面内容从文章管理换成了资产和任务管理。