@TOC
1. Vue2 中的 Vue Router (v3)
Vue Router 是 Vue.js 官方的路由管理器,专为构建单页应用(SPA)而设计。在 Vue2 生态中,vue-router@3 是标准选择。它通过声明式路由配置、组件化导航和灵活的守卫机制,实现了页面间的无刷新跳转。
下面我们将从安装开始,逐步展示 Vue Router v3 的核心用法,并结合实际代码说明其工作方式。
1.安装与基本配置
要使用 Vue Router,首先需要通过 npm 安装 vue-router@3 版本,并创建一个集中式的路由配置文件。该文件定义了路径(path)、名称(name)与组件(component)之间的映射关系,是整个应用导航系统的“地图”。
此外,我们还会设置路由模式(history/hash)、基础路径以及滚动行为等高级选项,确保用户体验一致。
npm install vue-router@3
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue') // 路由懒加载
},
{
path: '/user/:id',
name: 'User',
component: () => import('../views/User.vue'),
props: true // 将路由参数自动作为 props 传入组件
},
{
path: '*',
redirect: '/' // 404 页面重定向
}
]
const router = new VueRouter({
mode: 'history', // 使用浏览器 History API 实现 URL 清爽化
base: process.env.BASE_URL, // 支持部署子目录
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition // 返回之前保存的位置(如前进/后退)
} else {
return {
x: 0, y: 0 } // 默认滚动到顶部
}
}
})
export default router
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router, // 将路由实例注入根实例
render: h => h(App)
}).$mount('#app')
至此,路由系统已初始化完成。接下来可以在模板中使用
<router-link>和<router-view>进行导航与内容渲染。
2.在组件中使用
在 Vue2 中,我们可以通过两种方式控制路由:声明式导航 和 编程式导航。
- 声明式导航 使用
<router-link>组件生成可点击的链接,类似 HTML 的<a>标签,但不会引起页面刷新。 - 编程式导航 则通过
this.$router提供的方法(如push,replace,go)实现逻辑跳转,适用于按钮事件或条件判断场景。
同时,this.$route 对象提供了当前路由的详细信息(如路径、参数、查询等),可用于动态响应路由变化。
<template>
<div id="app">
<nav>
<!-- 声明式导航 -->
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-link :to="{ name: 'User', params: { id: 123 }}">User</router-link>
<!-- 活动路由样式 -->
<router-link to="/" exact-active-class="active">Home</router-link>
</nav>
<!-- 路由出口:匹配的组件将在此处渲染 -->
<router-view/>
</div>
</template>
<script>
export default {
methods: {
// 编程式导航
goToAbout() {
this.$router.push('/about')
},
goBack() {
this.$router.go(-1) // 后退一页
},
replaceRoute() {
this.$router.replace('/about') // 替换当前记录,无法后退
}
},
// 访问路由信息
computed: {
currentRoute() {
return this.$route // 包含 path, params, query, meta 等
}
}
}
</script>
注意:
$route是响应式的,当 URL 变化时会自动更新,适合用于监听参数变化。
3.路由守卫
路由守卫是 Vue Router 的强大功能之一,允许你在导航过程中插入逻辑钩子,常用于权限校验、页面提示、数据预加载等场景。
Vue Router v3 提供了多种粒度的守卫:
- 全局守卫:作用于所有路由切换
- 路由独享守卫:仅针对特定路由
- 组件内守卫:写在组件内部,便于处理组件级别的逻辑
这些守卫函数必须调用 next() 才能继续导航,否则会被阻塞。
1.全局前置守卫
这是最常用的守卫,通常用于登录验证。
router.beforeEach((to, from, next) => {
const isAuthenticated = checkAuth()
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login') // 未登录则跳转至登录页
} else {
next() // 放行
}
})
2.全局解析守卫
在确认最终目标路由之前执行,较少使用。
router.beforeResolve((to, from, next) => {
next()
})
3.全局后置钩子
不接收 next 函数,不能阻止导航,常用于统计分析。
router.afterEach((to, from) => {
console.log(`页面跳转:${
from.path} → ${
to.path}`)
})
4.路由独享守卫
直接定义在某个路由配置上,适用于管理员页面等特殊权限控制。
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (!isAdmin()) {
next('/403')
} else {
next()
}
}
}
]
5. 组件内守卫
写在组件选项中,用于处理组件自身的生命周期与路由交互。
const User = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 此时尚未创建组件实例,不能访问 this
next(vm => {
// 通过 vm 访问组件实例,可用于初始化数据
})
},
beforeRouteUpdate(to, from, next) {
// 当前组件复用时触发(如 /user/1 → /user/2)
this.userData = null
next()
},
beforeRouteLeave(to, from, next) {
// 离开当前路由前询问用户
const answer = window.confirm('确定要离开吗?')
if (answer) {
next()
} else {
next(false) // 阻止导航
}
}
}
特别注意:
beforeRouteEnter中的this为undefined,需通过next(callback)获取实例。
2. Vue3 中的 Vue Router (v4)
随着 Vue3 的发布,vue-router@4 也随之推出,全面适配 Composition API 和新的应用实例创建方式。相比 v3,v4 更加现代化,API 设计更加函数化和类型友好。
它不再依赖 Vue 插件机制中的 Vue.use(),而是通过 createApp().use(router) 注入,并原生支持 TypeScript 和组合式 API。
下面我们来看如何在 Vue3 项目中配置和使用 Vue Router v4。
1.安装与基本配置
首先安装 vue-router@4,然后使用 createRouter 工厂函数创建路由实例。历史模式不再通过字符串配置,而是显式调用 createWebHistory 或 createWebHashHistory。
这种设计更符合现代 JavaScript 模块化理念,也更容易做 SSR 和测试隔离。
npm install vue-router@4
// router/index.js
import {
createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: {
requiresAuth: true
}
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
},
{
path: '/user/:id',
name: 'User',
component: () => import('../views/User.vue'),
props: true
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), // 显式创建 history 实例
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {
top: 0, left: 0 } // API 更语义化:top/left 替代 x/y
}
}
})
export default router
// main.js
import {
createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router) // 使用 .use() 注册插件
app.mount('#app')
新的 API 更加清晰且易于扩展,尤其适合大型项目和 TypeScript 用户。
2.组合式 API 使用
Vue3 的一大亮点是 Composition API,它让逻辑复用和状态管理更加灵活。Vue Router v4 完美支持这一特性,提供了 useRouter 和 useRoute 两个组合式函数,可在 setup() 中直接调用。
这使得我们可以像使用 Pinia 或 Vuex 一样,在任何地方轻松访问路由状态和方法。
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<!-- 使用 v-slot 获取当前组件,便于添加过渡动画 -->
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
<script>
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
const goToAbout = () => {
router.push('/about')
}
const goBack = () => {
router.go(-1)
}
// 监听路由参数变化,实时获取新用户数据
watch(
() => route.params.id,
(newId) => {
fetchUserData(newId)
},
{ immediate: true }
)
return {
goToAbout,
goBack,
currentRoute: route
}
}
}
</script>
推荐在 Vue3 项目中优先使用
useRouter和useRoute,它们比this.$router更简洁、类型推导更强。
3.路由守卫
Vue Router v4 对守卫机制进行了简化和现代化改造。最大的变化是:不再强制要求调用 next(),而是通过返回值来决定导航行为。
你可以直接返回一个路径字符串、布尔值或 false 来中断导航,语义更清晰,避免了“忘记调用 next”的常见错误。
1.全局守卫(返回方式)
router.beforeEach((to, from) => {
const isAuthenticated = checkAuth()
if (to.meta.requiresAuth && !isAuthenticated) {
return '/login' // 自动跳转到登录页
}
// 不需要 return 或 next(),默认放行
})
2.组合式 API 中的导航守卫
除了全局守卫,你还可以在 setup() 中使用 onBeforeRouteLeave 和 onBeforeRouteUpdate,它们的行为与组件内守卫一致,但更适合函数式组件或逻辑抽离。
import {
onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
export default {
setup() {
onBeforeRouteLeave((to, from) => {
const answer = window.confirm('确定要离开吗?')
if (!answer) return false // 阻止导航
})
onBeforeRouteUpdate(async (to, from) => {
// 处理路由参数更新,例如刷新用户数据
await refreshUserData(to.params.id)
})
}
}
新的守卫语法更安全、更易读,推荐在 Vue3 项目中全面采用。
3. 核心原理分析
理解 Vue Router 的底层原理有助于我们更好地调试问题、优化性能,甚至自研轻量级路由库。
无论是 Vue2 的 v3 还是 Vue3 的 v4,其核心思想是一致的:
- 监听 URL 变化(hashchange / popstate)
- 匹配路由规则
- 更新视图组件
不同的是,Vue3 版本利用了 Composition API 和响应式系统(ref, reactive)重构了内部状态管理,使其更加高效和模块化。
下面我们分别剖析两个版本的核心实现思路。
1.Vue Router 3 (Vue2) 原理
在 Vue2 中,Vue Router 本质是一个 Vue 插件,通过 Vue.use() 注册后,会将 $router 和 $route 注入所有组件实例。
它的核心是基于观察者模式和原型继承构建的。VueRouter 类负责管理路由状态,History 子类负责处理不同模式下的 URL 监听与跳转。
以下是一个高度简化的模拟实现,展示了关键流程。
// 简化版原理实现
class VueRouter {
constructor(options) {
this.options = options
this.routeMap = {
}
this.app = null
this.apps = []
this.mode = options.mode || 'hash'
this.history = this.mode === 'history'
? new HTML5History(this)
: new HashHistory(this)
this.init()
}
init() {
const setupListeners = () => {
this.history.setupListeners()
}
this.history.transitionTo(
this.history.getCurrentLocation(),
setupListeners
)
}
match(raw) {
return this.history.match(raw)
}
push(location) {
this.history.push(location)
}
replace(location) {
this.history.replace(location)
}
}
// Hash 模式实现
class HashHistory {
constructor(router) {
this.router = router
ensureSlash()
}
getCurrentLocation() {
return getHash()
}
setupListeners() {
window.addEventListener('hashchange', () => {
this.transitionTo(getHash())
})
}
push(location) {
window.location.hash = location
}
}
// 路由匹配器
class RouteMatcher {
constructor(routes) {
this.routes = routes
this.matcher = createMatcher(routes)
}
match(location) {
return this.matcher.match(location)
}
}
关键点:
- 利用
defineReactive让$route成为响应式对象transitionTo方法驱动路由变更并触发组件重新渲染
2.Vue Router 4 (Vue3) 原理
Vue Router v4 彻底拥抱了 Vue3 的响应式系统和依赖注入机制。它不再修改原型链,而是通过 provide/inject 将路由实例传递给子孙组件。
同时,currentRoute 使用 ref() 创建,天然具备响应性,无需手动劫持属性。
整体架构更加函数化、模块化,易于 tree-shaking 和单元测试。
// 简化版原理实现
function createRouter(options) {
const router = {
// 使用 ref 包装当前路由,使其响应式
currentRoute: ref(START_LOCATION),
// 路由选项
options,
// 路由匹配器
matcher: createRouterMatcher(options.routes, options),
// 安装方法:注册全局组件并提供依赖
install(app) {
app.component('RouterLink', RouterLink)
app.component('RouterView', RouterView)
// 兼容选项式 API
app.config.globalProperties.$router = router
app.config.globalProperties.$route = router.currentRoute
// 提供组合式 API 支持
app.provide(routerKey, router)
app.provide(routeLocationKey, router.currentRoute)
},
push(to) {
return pushWithRedirect(to)
},
replace(to) {
return pushWithRedirect(to, 'replace')
},
go(delta) {
history.go(delta)
}
}
return router
}
// 路由匹配器
function createRouterMatcher(routes, globalOptions) {
const matchers = []
function addRoute(record, parent) {
const matcher = {
record,
parent,
children: []
}
if (parent) {
parent.children.push(matcher)
} else {
matchers.push(matcher)
}
return matcher
}
function resolve(location, currentLocation) {
// 解析路由路径,返回匹配结果
}
return {
addRoute,
resolve,
getRoutes() {
return matchers
}
}
}
// 组合式 API 函数
function useRouter() {
return inject(routerKey)
}
function useRoute() {
return inject(routeLocationKey)
}
优势总结:
- 更好的 TypeScript 支持
- 更小的包体积(tree-shaking 友好)
- 更强的可测试性和扩展性
4. 主要差异对比
为了帮助开发者快速掌握 Vue Router v3 与 v4 的区别,以下是两者的关键特性对比表。如果你正在迁移项目或学习新技术,这张表将为你提供明确指引。
| 特性 | Vue Router 3 (Vue2) | Vue Router 4 (Vue3) |
|---|---|---|
| 创建方式 | new VueRouter() |
createRouter() |
| 历史模式 | mode: 'history' |
history: createWebHistory() |
| TypeScript 支持 | 需要额外类型定义 | 内置完整类型支持 |
| 组合式 API | 不支持 | 支持 useRouter(), useRoute() |
| 路由守卫 | 必须调用 next() |
可返回布尔值或路径 |
| 动态路由 | router.addRoutes() |
router.addRoute()(更安全) |
| Scroll 行为 | 返回位置对象 { x, y } |
返回 { top, left },API 更一致 |
| 路由组件 | 单一 <router-view> |
支持作用域插槽 v-slot="{ Component }" |
建议:新项目一律使用 Vue Router v4;旧项目升级时注意守卫语法和懒加载兼容性。
5. 高级用法
掌握了基础之后,我们可以进一步探索一些高级技巧,提升应用的功能性和用户体验。
1.路由懒加载
路由懒加载是性能优化的重要手段。通过动态 import() 语法,可以让每个路由对应的组件按需加载,减少首屏体积。
// Vue2 & Vue3 通用
const routes = [
{
path: '/admin',
component: () => import(/* webpackChunkName: "admin" */ '@/views/Admin.vue')
}
]
构建工具(如 Webpack/Vite)会自动将其拆分为独立 chunk,只在访问时加载。
2.路由元信息
meta 字段可用于存储任意附加信息,如权限要求、页面标题、是否需要缓存等,极大增强了路由的表达能力。
const routes = [
{
path: '/dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
title: '控制面板',
permissions: ['admin', 'user']
}
}
]
// 在路由守卫中统一处理
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !isLoggedIn()) {
return '/login'
}
// 设置页面标题
if (to.meta.title) {
document.title = to.meta.title
}
})
推荐在大型项目中建立统一的
meta规范,提高可维护性。
3.动态路由
有时我们需要根据用户角色或配置动态添加路由,比如后台管理系统中按权限加载菜单。
Vue Router v4 提供了更安全的动态路由 API。
// Vue3 动态添加路由
router.addRoute({
path: '/new-route',
component: () => import('@/views/NewRoute.vue')
})
// 添加嵌套路由
router.addRoute('parent', {
path: 'child',
component: () => import('@/views/Child.vue')
})
// 删除路由
const removeRoute = router.addRoute(routeRecord)
removeRoute()
// 检查路由是否存在
router.hasRoute('route-name')
注意:动态路由应配合权限校验使用,防止恶意注入。
6. 使用建议
良好的工程实践能让路由系统更加健壮、易维护。以下是一些推荐的最佳实践。
1.路由模块化
随着项目增长,路由配置可能变得臃肿。建议按功能模块拆分路由文件,再统一导入主路由。
// router/modules/user.js
export default {
path: '/user',
name: 'User',
component: () => import('@/views/User/index.vue'),
children: [
{
path: 'profile',
component: () => import('@/views/User/Profile.vue')
}
]
}
// router/index.js
import userRoutes from './modules/user'
const routes = [
...userRoutes
]
模块化结构清晰,便于团队协作和权限划分。
2. 权限控制
结合路由元信息和全局守卫,可以实现细粒度的权限控制系统。
// 权限路由守卫
router.beforeEach(async (to, from) => {
// 检查是否需要认证
if (to.meta.requiresAuth) {
const token = localStorage.getItem('token')
if (!token) {
return '/login'
}
// 检查用户权限
const userRole = await getUserRole()
if (to.meta.roles && !to.meta.roles.includes(userRole)) {
return '/403'
}
}
})
建议服务端也做权限校验,前端仅为体验优化。
3.错误处理
路由操作可能出现异常,如导航重复、异步组件加载失败等,应妥善捕获并处理。
// 全局路由错误监听
router.onError((error) => {
console.error('路由错误:', error)
})
// 导航失败处理
router.push('/some-path').catch(err => {
if (err.name !== 'NavigationDuplicated') {
console.error('导航失败:', err)
}
})
NavigationDuplicated是 Vue Router v3 的常见警告,v4 已修复。
7. 性能优化
高性能的 SPA 不仅依赖框架本身,还需要合理的路由策略。
1.路由懒加载分组
对于功能相近的页面,可以使用相同的 webpackChunkName 进行分组打包,避免过多小文件影响加载效率。
const routes = [
{
path: '/admin',
component: () => import(/* webpackChunkName: "admin-group" */ '@/views/Admin.vue')
},
{
path: '/user',
component: () => import(/* webpackChunkName: "admin-group" */ '@/views/User.vue')
}
]
分组后,多个相关页面共用一个 chunk,提升缓存利用率。
2.预加载策略
可以根据用户行为预测下一步可能访问的页面,提前加载资源。
router.beforeEach((to, from) => {
if (to.meta.preload) {
// 预加载图片、API 数据或其他资源
preloadAssets(to.meta.preload)
}
})
结合 Intersection Observer 或鼠标悬停事件,可实现智能预加载。
8. 总结
通过深入理解 Vue Router 在 Vue2 和 Vue3 中的用法和底层原理,开发者可以:
- 更好地组织单页应用(SPA)结构
- 实现高效的路由导航与状态管理
- 构建权限控制、懒加载、SEO 友好的现代前端应用
- 利用组合式 API 提升代码可维护性与复用性
Vue Router 从 v3 到 v4 的演进体现了对 Vue3 Composition API、TypeScript 支持以及现代化开发体验的全面升级,是构建高质量 Vue 应用不可或缺的核心工具。
下次当你点击一个
<router-link>时,不妨想一想背后那场精妙的“URL 监听 → 路由匹配 → 组件切换”的旅程。