Vue Router完全指南 —— 从基础配置到权限控制
前面两篇我分为讲了vue的一些基本和常用的语法,现在这篇文章带你了解vue的路由管理是怎么实现的
引言
在 Vue 开发单页应用(SPA)时,“页面跳转不刷新、URL 与组件联动” 是核心需求 —— 而 Vue Router 就是实现这一需求的官方工具。它通过管理 “路径(path)与组件(component)” 的对应关系,让我们能像浏览多页网站一样切换内容,却始终保持单页应用的流畅性。本文将从基础配置入手,一步步解锁路由导航、传参、嵌套、守卫等进阶能力,帮你搞定任何 Vue 项目的路由管理。
一、先搞懂:Vue Router 核心概念
在写代码前,我们得先理清几个关键概念,避免后续混淆。
1.1 什么是路由?
路由本质是一组 key-value 的对应关系。在前端路由中:
key
:URL 路径(如/home
)value
:对应渲染的组件(如Home.vue
)
多个路由需要通过 “路由器” 统一管理,Vue Router 就是这个 “路由器”,负责监听 URL 变化,渲染对应的组件。
1.2 SPA 应用的特点
Vue Router 服务于 SPA(单页 Web 应用),这类应用有 4 个核心特点:
- 整个应用只有 1 个完整 HTML 页面(
index.html
); - 点击导航不刷新页面,只做局部内容更新;
- URL 变化通过前端路由控制,而非后端跳转;
- 数据需通过 Ajax/axios 异步获取。
1.3 路由项目目录结构
一个标准的 Vue+Vue Router 项目结构如下(重点关注router
目录):
src/
├── router/ # 路由配置目录
│ └── index.ts # 路由规则与路由器创建
├── views/ # 路由页面组件(如Home.vue、About.vue)
├── components/ # 通用功能组件
├── App.vue # 根组件(放导航与路由出口)
└── main.ts # 入口文件(注册路由器)
二、入门:Vue Router 4 基础使用
从安装到实现第一个路由跳转,只需 4 步,全程可复制使用。
2.1 步骤 1:安装 Vue Router 4
Vue 3 对应的路由版本是 Vue Router 4,通过 npm 安装:
npm install vue-router@4
2.2 步骤 2:创建路由器
在src/router/index.ts
中,用createRouter
创建路由器,定义路由规则(routes
):
// src/router/index.ts
import { createRouter, createWebHistory } from "vue-router";
// 导入路由页面组件
import Home from "@/views/Home.vue";
import News from "@/views/News.vue";
import About from "@/views/About.vue";// 1. 定义路由规则:path -> component的对应关系
const routes = [{path: "/home", // URL路径name: "home", // 命名路由(可选,用于简化跳转)component: Home // 对应组件},{path: "/news",name: "news",component: News},{path: "/about",name: "about",component: About}
];// 2. 创建路由器
const router = createRouter({history: createWebHistory(), // 路由模式(history模式,无#)routes // 传入路由规则
});// 3. 导出路由器,供main.ts注册
export default router;
2.3 步骤 3:注册路由器
在入口文件src/main.ts
中,通过app.use(router)
注册路由器,让整个应用支持路由
// src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router"; // 导入路由器const app = createApp(App);
app.use(router); // 注册路由器
app.mount("#app");
2.4 步骤 4:使用路由(导航 + 展示)
在根组件App.vue
中,用两个核心组件实现路由功能:
RouterLink
:替代<a>
标签的导航组件,点击不刷新页面;RouterView
:路由组件的 “容器”,URL 匹配的组件会渲染在这里。
代码示例:
<template><div class="app"><h1>Vue Router 测试</h1><!-- 1. 导航区:用RouterLink实现跳转 --><div class="nav"><!-- active-class:跳转后添加的激活样式 --><RouterLink to="/home" active-class="active">首页</RouterLink><RouterLink to="/news" active-class="active">新闻</RouterLink><RouterLink to="/about" active-class="active">关于</RouterLink></div><!-- 2. 展示区:RouterView渲染匹配的组件 --><div class="content"><RouterView></RouterView></div></div>
</template><script setup lang="ts" name="App">
// 导入RouterLink和RouterView(setup语法糖需手动导入)
import { RouterLink, RouterView } from "vue-router";
</script><style scoped>
.nav { margin: 20px 0; }
.nav a { margin-right: 20px; text-decoration: none; }
.active { color: #42b983; font-weight: bold; } /* 激活样式 */
</style>
三、核心配置:路由模式与跳转方式
这部分解决两个常见问题:“URL 带不带 #?”“怎么简化跳转写法?”
3.1 路由模式:history vs hash
Vue Router 提供两种 URL 模式,核心区别是 URL 是否带#
,需在创建路由器时配置:
模式 | 配置代码 | 优点 | 缺点 |
---|---|---|---|
history 模式 | history: createWebHistory() | URL 无 #,美观,接近传统网站 | 上线需服务端配置(否则刷新 404) |
hash 模式 | history: createWebHashHistory() | 兼容性好,无需服务端处理 | URL 带 #,SEO 优化较差 |
注意:history 模式需服务端配置(如 Nginx 转发所有请求到index.html
),否则用户刷新非首页会报 404;hash 模式无需额外配置,适合快速原型或兼容性要求高的场景。
3.2 路由跳转:to 的两种写法
RouterLink
的to
属性支持两种写法,可根据场景选择:
写法 1:字符串写法(简单场景)
直接写完整路径,适合无参数的跳转:
<!-- 字符串写法:直接指定路径 -->
<RouterLink to="/home">首页</RouterLink>
<!-- 带query参数的字符串写法(后面讲传参时详细说) -->
<RouterLink to="/news/detail?id=1&title=新闻标题">新闻详情</RouterLink>
写法 2:对象写法(复杂场景)
当需要传参数或用 “命名路由” 简化路径时,用对象写法更灵活:
<!-- 1. 仅指定路径 -->
<RouterLink :to="{ path: '/home' }">首页</RouterLink><!-- 2. 用命名路由(需先给路由配置name) -->
<RouterLink :to="{ name: 'home' }">首页</RouterLink>
<!-- 优势:无需写完整路径,路径变了也不用改这里 --><!-- 3. 带参数(后面传参章节详解) -->
<RouterLink :to="{ name: 'newsDetail', params: { id: 1, title: '新闻标题' }
}">新闻详情</RouterLink>
3.3 命名路由:简化跳转的 “快捷键”
给路由规则配置name
属性(命名路由),可替代完整路径,减少路径修改时的工作量。
步骤 1:给路由配置 name
// src/router/index.ts
const routes = [{name: "home", // 命名路由:name唯一,建议语义化path: "/home",component: Home},{name: "newsDetail", // 新闻详情的命名路由path: "/news/detail/:id/:title", // 带params占位(后面讲)component: NewsDetail}
];
步骤 2:用 name 跳转
<!-- 无需写完整路径,用name即可 -->
<RouterLink :to="{ name: 'newsDetail', params: { id: 1, title: '标题' } }">新闻详情
</RouterLink>
优势:若后续/news/detail
路径改成/news/content
,只需修改路由配置的path
,所有用name
跳转的代码无需改动,降低维护成本。
四、进阶:嵌套路由与路由传参
这部分解决 “页面内嵌套子组件”(如新闻列表→新闻详情)和 “页面间传递数据”(如列表→详情传 ID)的核心需求。
4.1 嵌套路由:实现 “父组件 - 子组件” 联动
场景:新闻页面(News.vue
)中,左侧是新闻列表,右侧是新闻详情,点击列表项切换详情 —— 这就需要嵌套路由(父路由/news
,子路由/news/detail
)。
步骤 1:配置嵌套路由(children)
在父路由(如/news
)中用children
配置子路由,子路由的path
无需加/
(会自动拼接父路由路径):
// src/router/index.ts
const routes = [{name: "news",path: "/news",component: News, // 父组件:新闻列表页// 子路由配置:children是数组,每个元素是子路由规则children: [{name: "newsDetail",path: "detail/:id/:title", // 子路由path:无需加/,拼接后是/news/detailcomponent: NewsDetail // 子组件:新闻详情页}]}
];
步骤 2:父组件中预留 “子路由出口”
父组件(News.vue
)中需添加RouterView
,子组件会渲染在这里:
<!-- News.vue(父组件) -->
<template><div class="news-page"><!-- 左侧:新闻列表(父组件内容) --><div class="news-list"><RouterLink v-for="item in newsList" :key="item.id":to="{ name: 'newsDetail', params: { id: item.id, title: item.title } }">{{ item.title }}</RouterLink></div><!-- 右侧:子路由出口(子组件NewsDetail会渲染在这里) --><div class="news-detail"><RouterView></RouterView></div></div>
</template><script setup lang="ts" name="News">
import { ref } from "vue";
import { RouterLink, RouterView } from "vue-router";// 模拟新闻列表数据
const newsList = ref([{ id: 1, title: "Vue Router 4 新特性" },{ id: 2, title: "SPA应用路由最佳实践" }
]);
</script>
4.2 路由传参:query vs params 两种方式
页面间传递数据(如列表→详情传 ID)是路由核心需求,Vue Router 提供两种传参方式,适用场景不同。
方式 1:query 参数(URL 可见,可选参数)
query 参数会拼在 URL 后面(如/news/detail?id=1&title=标题
),类似 GET 请求参数,适合传递 “可选参数”(如筛选条件、分页)。
步骤 1:传递 query 参数
<!-- 写法1:字符串写法(简单) -->
<RouterLink to="/news/detail?id=1&title=Vue Router 教程">新闻详情</RouterLink><!-- 写法2:对象写法(推荐,参数清晰) -->
<RouterLink :to="{ path: '/news/detail', // 或用name: 'newsDetail'query: { id: 1, title: 'Vue Router 教程' }
}">新闻详情</RouterLink>
步骤 2:接收 query 参数
在子组件(NewsDetail.vue
)中,用useRoute
钩子获取路由信息,从route.query
中取参数:
<!-- NewsDetail.vue -->
<script setup lang="ts" name="NewsDetail">
import { useRoute } from "vue-router";// 1. 获取路由实例(包含参数、路径等信息)
const route = useRoute();// 2. 接收query参数(注意:参数类型都是字符串)
const newsId = route.query.id; // "1"
const newsTitle = route.query.title; // "Vue Router 教程"console.log("新闻ID:", newsId);
</script>
方式 2:params 参数(URL 占位,必需参数)
params 参数通过 “路径占位” 传递(如/news/detail/1/标题
),URL 中不显示参数名,适合传递 “必需参数”(如资源 ID,符合 RESTful 风格)。
步骤 1:路由规则中 “占位”
先在子路由的path
中用:参数名
占位,声明需要接收的 params 参数:
// src/router/index.ts
const routes = [{name: "news",path: "/news",component: News,children: [{name: "newsDetail",// 占位::id和:title对应params的两个参数path: "detail/:id/:title", component: NewsDetail}]}
];
步骤 2:传递 params 参数
<!-- 写法1:字符串写法(需按占位顺序传) -->
<RouterLink :to="`/news/detail/${item.id}/${item.title}`">{{ item.title }}
</RouterLink><!-- 写法2:对象写法(必需用name,不能用path!) -->
<RouterLink :to="{ name: 'newsDetail', // 注意:params传参不能用pathparams: { id: item.id, title: item.title }
}">{{ item.title }}
</RouterLink>
步骤 3:接收 params 参数
与 query 类似,从route.params
中取参数:
<script setup lang="ts" name="NewsDetail">
import { useRoute } from "vue-router";const route = useRoute();// 接收params参数(类型也是字符串)
const newsId = route.params.id; // "1"
const newsTitle = route.params.title; // "Vue Router 教程"
</script>
query vs params 核心对比
对比维度 | query 参数 | params 参数 |
---|---|---|
URL 显示 | ?id=1&title=标题 | /1/标题 (路径占位) |
路由配置 | 无需额外配置 | 需在 path 中占位(:id/:title ) |
跳转方式 | 支持 path/name | 仅支持 name(对象写法) |
参数必要性 | 可选(可不传) | 必需(不传会导致路径错误) |
适用场景 | 筛选条件、分页、可选参数 | 资源 ID、必需参数(RESTful) |
五、优化:路由 props 与编程式导航
这部分解决 “组件解耦” 和 “非点击触发跳转”(如登录成功后跳转)的需求。
5.1 路由 props:让组件 “少依赖路由实例”
默认情况下,组件需通过route.query
/route.params
取参数,导致组件与路由强耦合(脱离路由就用不了)。用props
配置可将路由参数 “注入” 为组件的 props,让组件更通用。
Vue Router 的props
支持三种写法,覆盖不同场景:
写法 1:布尔值写法(仅 params 参数)
当props: true
时,路由的 params 参数会自动转为组件的 props:
// 1. 路由配置:props: true
const routes = [{name: "newsDetail",path: "detail/:id/:title",component: NewsDetail,props: true // 开启props,params参数转为组件props}
];// 2. 组件接收:用defineProps
<script setup lang="ts" name="NewsDetail">
// 直接接收props,无需依赖useRoute
defineProps(["id", "title"]);// 使用时直接用id/title,不用route.params
console.log("新闻ID:", id); // 无需.route.params.id
</script>
写法 2:对象写法(固定参数)
传递固定的静态参数,适合参数不依赖路由的场景:
// 路由配置:props是对象
const routes = [{path: "/user",component: User,props: { role: "guest", status: "active" } // 固定参数}
];// 组件接收
<script setup lang="ts" name="User">
defineProps(["role", "status"]);
console.log(role); // "guest"
</script>
写法 3:函数写法(灵活处理参数)
支持自定义参数逻辑,可整合 query/params 参数,或对参数加工:
// 路由配置:props是函数,接收route参数
const routes = [{path: "/news/detail",component: NewsDetail,// 函数返回的对象会作为props传给组件props: (route) => ({id: route.query.id, // 取query参数title: route.query.title,time: new Date().toLocaleString() // 额外加动态参数})}
];// 组件接收
<script setup lang="ts" name="NewsDetail">
defineProps(["id", "title", "time"]);
console.log(time); // 当前时间
</script>
优势:组件无需导入useRoute
,脱离路由环境也能通过 props 传参使用(如单元测试、组件复用)。
5.2 编程式导航:非点击触发跳转
除了RouterLink
(声明式导航),还可通过代码触发跳转(如登录成功后、接口请求成功后),这就是编程式导航,核心是useRouter
钩子。
步骤 1:导入 useRouter 与 useRoute
<script setup lang="ts">
import { useRouter, useRoute } from "vue-router";const router = useRouter(); // 用于跳转(控制路由)
const route = useRoute(); // 用于获取当前路由信息(参数、路径等)
</script>
步骤 2:常用编程式导航方法
// 1. 跳转到指定路径(字符串写法)
router.push("/home");// 2. 跳转到指定路径(对象写法,带query参数)
router.push({path: "/news/detail",query: { id: 1, title: "标题" }
});// 3. 用命名路由跳转(带params参数)
router.push({name: "newsDetail",params: { id: 1, title: "标题" }
});// 4. 替换当前历史记录(不会新增历史条目,后退不会回到当前页)
// 场景:登录页→首页,后退不想回到登录页
router.replace("/home");// 5. 后退/前进(类似浏览器的前进后退按钮)
router.go(-1); // 后退1步
router.go(1); // 前进1步
router.back(); // 等价于go(-1)
router.forward(); // 等价于go(1)
六、异常处理:重定向与 404 页面
处理 “默认首页” 和 “无效路径” 的场景,提升用户体验。
6.1 重定向:默认路径跳转
当用户访问根路径(/
)时,自动跳转到首页(/home
),用redirect
配置:
// src/router/index.ts
const routes = [// 重定向:访问/时,自动跳转到/home{ path: "/", redirect: "/home" },// 其他路由规则{ path: "/home", component: Home },{ path: "/about", component: About }
];
也支持重定向到命名路由:
{ path: "/", redirect: { name: "home" } }
6.2 404 页面:无效路径处理
当用户访问不存在的路径时,显示 404 页面,需用 “通配符” 匹配所有未定义的路径。
步骤 1:创建 404 组件(NotFound.vue)
<!-- src/views/NotFound.vue -->
<template><div class="not-found"><h1>404</h1><p>页面不存在</p><RouterLink to="/home">返回首页</RouterLink></div>
</template>
步骤 2:配置通配符路由
通配符/:pathMatch(.*)*
会匹配所有未定义的路径,需放在路由规则的最后面(路由匹配按顺序执行):
// src/router/index.ts
import NotFound from "@/views/NotFound.vue";const routes = [// 其他路由规则...// 404页面:放在最后面{path: "/:pathMatch(.*)*", // 通配符,匹配所有未定义路径name: "NotFound",component: NotFound}
];
七、权限控制:路由守卫的核心应用
路由守卫是 Vue Router 的 “安全门”,可在路由跳转前 / 后执行逻辑(如登录验证、角色权限判断),核心是控制 “谁能访问哪个页面”。
7.1 路由守卫的分类与执行顺序
Vue Router 提供 3 类守卫,执行顺序如下(从跳转开始到结束):
- 全局前置守卫(beforeEach):所有路由跳转前执行(最常用,如登录验证);
- 路由独享守卫(beforeEnter):仅当前路由跳转前执行(单独配置某路由);
- 组件内守卫(beforeRouteEnter):进入组件前执行(组件内配置);
- 全局解析守卫(beforeResolve):所有路由跳转前,组件内守卫执行后;
- 全局后置钩子(afterEach):所有路由跳转后执行(无导航控制能力,如修改页面标题)。
7.2 最常用:全局前置守卫(beforeEach)
全局前置守卫是权限控制的核心,在src/router/index.ts
中配置,每次路由跳转前都会触发,可控制 “是否允许跳转”。
实战:登录验证(未登录跳登录页)
// src/router/index.ts
import { createRouter, createWebHistory } from "vue-router";
import Home from "@/views/Home.vue";
import Login from "@/views/Login.vue";const routes = [{ path: "/", redirect: "/home" },{ path: "/home", component: Home },{ path: "/about", component: () => import("@/views/About.vue") }, // 懒加载{ path: "/login", name: "Login", component: Login },// 需要登录才能访问的页面:添加meta.requiresAuth标记{path: "/user/center",component: () => import("@/views/UserCenter.vue"),meta: { requiresAuth: true } // 标记:需登录才能访问},{ path: "/:pathMatch(.*)*", component: () => import("@/views/NotFound.vue") }
];const router = createRouter({history: createWebHistory(),routes
});// 全局前置守卫:登录验证
router.beforeEach(async (to, from, next) => {// to:即将进入的目标路由(要去哪里)// from:当前离开的路由(从哪里来)// next:控制导航的函数(允许/禁止/重定向)// 1. 检查目标路由是否需要登录const needLogin = to.meta.requiresAuth;if (needLogin) {// 2. 检查本地是否有登录令牌(token)const token = localStorage.getItem("token");if (!token) {// 3. 未登录:重定向到登录页,并携带“当前路径”(登录后返回)next({name: "Login",query: { redirect: to.fullPath } // redirect:记录要访问的页面});return;}// 4. 已登录:验证token有效性(模拟接口)const isTokenValid = await mockValidateToken(token);if (!isTokenValid) {// token无效:清除token,重定向到登录页localStorage.removeItem("token");next({ name: "Login", query: { redirect: to.fullPath } });return;}}// 5. 无需登录或已登录:允许跳转next();
});// 模拟token验证接口
const mockValidateToken = (token: string) => {return new Promise((resolve) => {// 实际项目中调用后端接口验证tokensetTimeout(() => {resolve(token === "valid-token"); // 假设valid-token是有效token}, 300);});
};export default router;
总结
Vue Router 是 Vue SPA 应用的 “骨架”,掌握它的核心配置(嵌套、传参、守卫),就能应对从简单博客到复杂管理系统的路由需求。建议从 “个人博客路由系统” 开始实践,逐步加入权限控制和面包屑导航,巩固所学知识点。