Skip to content

24|Vue 路由与导航守卫

单页应用(SPA)只有一个 HTML 页面,页面切换通过 JavaScript 替换内容实现,不需要整页刷新。Vue Router 是 Vue 官方的路由库,负责把 URL 映射到对应组件,管理浏览器历史记录,并提供导航守卫在路由切换前后执行逻辑。

一、安装和基本配置

bash
npm install vue-router@4
js
// router/index.js
import { createRouter, createWebHistory } from "vue-router";
import ServerList from "../views/ServerList.vue";
import TaskList from "../views/TaskList.vue";
import Login from "../views/Login.vue";

const routes = [
  { path: "/", redirect: "/servers" },
  { path: "/login", component: Login },
  { path: "/servers", component: ServerList },
  { path: "/tasks", component: TaskList },
  { path: "/servers/:id", component: ServerDetail },   // 动态路由
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

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

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

二、路由视图和链接

vue
<!-- App.vue -->
<template>
  <nav>
    <RouterLink to="/servers">服务器</RouterLink>
    <RouterLink to="/tasks">任务</RouterLink>
  </nav>

  <!-- 当前路由对应的组件渲染在这里 -->
  <RouterView />
</template>
组件作用
RouterLink生成路由链接,点击不会刷新页面
RouterView占位符,显示当前路由匹配的组件

RouterLink 渲染为 a 标签,但会拦截点击事件,用 JavaScript 切换路由。

激活状态样式

vue
<RouterLink to="/servers" active-class="active-link">服务器</RouterLink>

当前路由匹配时,会自动添加 active-class 指定的类名。

三、动态路由

js
const routes = [
  { path: "/servers/:id", component: ServerDetail },
];

:id 是路由参数,可以匹配 /servers/1/servers/web-01 等路径。

在组件中读取参数:

vue
<script setup>
import { useRoute } from "vue-router";

const route = useRoute();
console.log(route.params.id);   // "1" 或 "web-01"
</script>

查询参数

js
// URL: /servers?page=2&limit=20
console.log(route.query.page);   // "2"
console.log(route.query.limit);  // "20"

查询参数都是字符串,需要数字时手动转换:

js
const page = Number(route.query.page) || 1;

四、编程导航

除了在模板中用 RouterLink,也可以在 JavaScript 中跳转:

vue
<script setup>
import { useRouter } from "vue-router";

const router = useRouter();

function goToDetail(id) {
  router.push(`/servers/${id}`);
}

function goBack() {
  router.back();
}
</script>
方法作用
router.push(path)跳转到新路由(加入历史栈)
router.replace(path)替换当前路由(不加入历史栈)
router.back()后退
router.go(n)前进/后退 n 步

五、导航守卫

在路由切换前后执行校验或准备工作:

全局前置守卫

js
// router/index.js
import { useUserStore } from "../stores/user.js";

router.beforeEach((to, from, next) => {
  const user = useUserStore();

  // 需要登录但未登录,重定向到登录页
  if (to.meta.requiresAuth && !user.isLoggedIn) {
    next("/login");
  } else {
    next();   // 放行
  }
});
参数含义
to即将进入的目标路由
from当前正要离开的路由
next()放行;next("/login") 重定向;next(false) 取消导航

路由元信息

在路由配置中标记额外信息:

js
const routes = [
  {
    path: "/servers",
    component: ServerList,
    meta: { requiresAuth: true, title: "服务器列表" },
  },
];

组件内守卫

vue
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from "vue-router";

// 离开当前页面前
onBeforeRouteLeave((to, from, next) => {
  if (hasUnsavedChanges.value) {
    const confirmed = confirm("有未保存的更改,确定离开?");
    next(confirmed);
  } else {
    next();
  }
});

// 当前路由参数变化时(如从 /servers/1 到 /servers/2)
onBeforeRouteUpdate((to, from) => {
  loadServerDetail(to.params.id);
});
</script>

六、懒加载

大型应用中,把所有组件打包成一个文件会导致首屏加载慢。路由懒加载让组件只在进入对应路由时才下载:

js
const routes = [
  {
    path: "/servers",
    component: () => import("../views/ServerList.vue"),
  },
];

import() 返回 Promise,Vue Router 在需要时自动等待组件加载完成。

七、常见错误

在 setup 之外使用 useRoute / useRouter

js
// 错误:必须在组件的 setup 期间调用
const route = useRoute();

// 正确
export default {
  setup() {
    const route = useRoute();   // OK
  },
};

导航守卫中忘记调用 next

js
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !isLoggedIn) {
    next("/login");
  }
  // 错误:未登录时 else 分支没有 next(),导航卡住
});

动态路由参数变化时组件不复用

js
// 从 /servers/1 跳转到 /servers/2,默认复用组件,不重新挂载
// 需要监听路由变化
import { watch } from "vue";
import { useRoute } from "vue-router";

const route = useRoute();
watch(() => route.params.id, (newId) => {
  loadDetail(newId);
}, { immediate: true });

或者用 onBeforeRouteUpdate 组件守卫。