您的位置:首页 > 路由器知识路由器知识

2023最新VueRouter3.x完全指南:从入门到精通的路由管理手册

2026-06-03人已围观

2023最新Vue Router 3.x完全指南:从入门到精通的路由管理手册

一、为什么需要Vue Router?

想象你正在逛一家超级商场(你的单页应用),每个店铺就是一个组件。没有路由系统的话,你每次换店铺都得重新进一次商场大门(页面刷新)。而Vue Router就像商场里的指示牌和导航员,让你在不同店铺间自由穿梭,却不用每次都重新进商场——这就是单页应用(SPA)的核心优势:无刷新页面切换。

传统网页用``标签跳转的方式,在Vue单页应用里会导致整个页面重新加载,就像你在手机App里切换页面却被要求重新登录一样糟糕。Vue Router通过管理URL的变化,让组件按需加载,实现了"页面切换如丝般顺滑"的用户体验。

二、安装与基础配置

2.1 安装Vue Router

npm安装(推荐):

```bash

npm install vue-router@3.x --save

```

yarn安装:

```bash

yarn add vue-router@3.x

```

如果你使用vue-cli创建项目,可以在初始化时直接勾选"Router"选项,脚手架会自动帮你配置好基础路由结构。

2.2 基础路由配置

在src目录下创建`router`文件夹,新建`index.js`文件:

```javascript

import Vue from 'vue'

import Router from 'vue-router'

// 导入组件

import Home from '@/views/Home.vue'

import About from '@/views/About.vue'

// 安装路由插件

Vue.use(Router)

// 定义路由规则

const routes = [

{

path: '/', // URL路径

name: 'Home', // 路由名称(可选)

component: Home // 对应的组件

},

{

path: '/about',

name: 'About',

component: About

}

]

// 创建路由实例

const router = new Router({

mode: 'history', // 路由模式:history或hash

routes // 路由规则数组

})

export default router

```

然后在`main.js`中引入并使用路由:

```javascript

import Vue from 'vue'

import App from './App.vue'

import router from './router' // 引入路由实例

new Vue({

router, // 注入路由

render: h => h(App)

}).$mount('app')

```

三、路由导航与视图

3.1 声明式导航:``

把传统的``标签替换成Vue Router提供的``组件:

```vue

```

``会被渲染成``标签,但有以下优势:

- 自动添加激活状态的class(默认`router-link-active`)

- 避免整页刷新

- 支持各种高级功能(如动画、延迟加载)

你可以自定义激活样式:

```vue

首页

```

3.2 编程式导航:`$router.push()`

在JavaScript代码中实现导航,就像你在App里点击按钮跳转到新页面:

```javascript

// 在组件方法中

methods: {

goToAbout() {

// 字符串路径

this.$router.push('/about')

// 命名路由 + 参数

this.$router.push({ name: 'About', params: { id: 123 } })

// 带查询参数,变成 /about?plan=private

this.$router.push({ path: '/about', query: { plan: 'private' } })

}

}

```

其他常用导航方法:

```javascript

// 替换当前历史记录(无法回退到上一页)

this.$router.replace('/about')

// 前进或后退

this.$router.go(-1) // 后退一页,类似浏览器的"后退"按钮

this.$router.go(1) // 前进一页

this.$router.go(0) // 刷新当前页

```

3.3 路由视图:``

``就像一个动态的"显示框",匹配的路由组件会在这里显示。你可以把它放在任何地方,实现复杂的布局:

```vue

```

四、动态路由匹配

4.1 基本用法

假设你有一个用户详情页,URL需要根据用户ID变化,比如`/user/1`、`/user/2`,这时就需要动态路由参数:

```javascript

// 路由配置

{

path: '/user/:id', // :id 就是动态参数

name: 'User',

component: User

}

```

在组件中获取参数:

```javascript

export default {

mounted() {

// 通过 this.$route.params 获取参数

console.log('用户ID:', this.$route.params.id)

}

}

```

你可以在路径中设置多个参数:

```javascript

{

path: '/user/:username/post/:postId',

component: UserPost

}

```

访问`/user/john/post/123`时,`$route.params`会是`{ username: 'john', postId: '123' }`。

4.2 响应路由参数变化

当从`/user/1`跳转到`/user/2`时,Vue会复用User组件(性能优化),这意味着组件的生命周期钩子(如`created`、`mounted`)不会重新执行。这就像你在微信里切换聊天窗口,聊天界面不会重新创建,但内容会更新。

有两种方法可以监听参数变化:

方法一:watch监听$route

```javascript

export default {

watch: {

'$route'(to, from) {

// to: 目标路由对象

// from: 源路由对象

this.loadUserData(to.params.id)

}

},

methods: {

loadUserData(userId) {

// 加载用户数据

}

}

}

```

方法二:导航守卫 beforeRouteUpdate

```javascript

export default {

beforeRouteUpdate(to, from, next) {

// 在路由更新前执行

this.loadUserData(to.params.id)

next() // 必须调用next()才能继续导航

}

}

```

4.3 参数校验

可以用正则表达式约束参数格式,比如只允许数字ID:

```javascript

{

// 只匹配数字ID

path: '/user/:id(\\d+)',

component: User

}

```

五、路由参数传递

5.1 动态路由参数(params)

适合传递重要参数(如ID),参数会作为URL的一部分:

```javascript

// 导航

this.$router.push({ name: 'User', params: { id: 123 } })

// 路由配置

{

path: '/user/:id',

name: 'User',

component: User

}

// 组件中获取

this.$route.params.id // "123"

```

5.2 查询参数(query)

适合传递非必需参数(如筛选条件),参数会以`?key=value`形式附加在URL后:

```javascript

// 导航

this.$router.push({ path: '/search', query: { keyword: 'vue', page: 1 } })

// 组件中获取

this.$route.query.keyword // "vue"

this.$route.query.page // "1"

```

5.3 props传参(推荐)

为了让组件更灵活复用,建议通过props接收路由参数,而不是直接使用`$route.params`:

路由配置:

```javascript

{

path: '/user/:id',

name: 'User',

component: User,

props: true // 启用props传参

}

```

组件中使用:

```vue

```

更高级的用法:函数式props,可以转换参数类型或组合参数:

```javascript

{

path: '/user/:id',

component: User,

props: route => ({

id: Number(route.params.id), // 转换为数字类型

fullPath: route.fullPath // 附加其他信息

})

}

```

六、嵌套路由

就像俄罗斯套娃,路由也可以嵌套。比如在Dashboard页面里,还有统计、设置等子页面:

```javascript

const routes = [

{

path: '/dashboard',

component: Dashboard,

children: [

{

// 当 /dashboard/stats 匹配成功

path: 'stats',

component: DashboardStats

},

{

// 当 /dashboard/settings 匹配成功

path: 'settings',

component: DashboardSettings

},

{

// 当 /dashboard 匹配成功,默认显示

path: '',

component: DashboardHome

}

]

}

]

```

在Dashboard组件中需要添加``来显示子路由内容:

```vue

```

七、路由守卫

路由守卫就像保安,在你进入或离开某个区域(路由)时进行检查。常见场景:登录验证、权限控制。

7.1 全局守卫

在`router/index.js`中定义,对所有路由生效:

```javascript

// 全局前置守卫

router.beforeEach((to, from, next) => {

// to: 要去的路由

// from: 来自的路由

// next: 放行函数

// 检查是否需要登录

if (to.meta.requiresAuth && !isLogin()) {

// 未登录,重定向到登录页

next({ name: 'Login', query: { redirect: to.fullPath } })

} else {

// 已登录,放行

next()

}

})

// 全局后置钩子(没有next参数)

router.afterEach((to, from) => {

// 可以在这里修改页面标题

document.title = to.meta.title || '默认标题'

})

```

7.2 路由独享守卫

在路由配置中定义,只对当前路由生效:

```javascript

{

path: '/admin',

component: Admin,

beforeEnter: (to, from, next) => {

// 检查是否为管理员

if (userRole !== 'admin') {

next('/403') // 不是管理员,跳转到403页面

} else {

next() // 是管理员,放行

}

}

}

```

7.3 组件内守卫

在组件内部定义,控制组件的进入和离开:

```javascript

export default {

// 进入组件前

beforeRouteEnter(to, from, next) {

// 此时组件实例还未创建,不能使用this

next(vm => {

// vm就是组件实例

vm.loadData()

})

},

// 路由更新时(动态参数变化)

beforeRouteUpdate(to, from, next) {

this.loadData(to.params.id)

next()

},

// 离开组件时

beforeRouteLeave(to, from, next) {

// 询问用户是否保存修改

if (this.hasUnsavedChanges) {

if (confirm('有未保存的修改,确定要离开吗?')) {

next()

} else {

next(false) // 取消导航

}

} else {

next()

}

}

}

```

八、路由模式

Vue Router提供两种主要路由模式:

8.1 Hash模式(默认)

URL中带有``,如`http://example.com//user/1`。后面的内容不会发送到服务器,兼容性好(支持所有浏览器)。

```javascript

const router = new Router({

mode: 'hash', // 默认值,可以省略

routes

})

```

8.2 History模式

URL中没有``,如`http://example.com/user/1`,更美观但需要服务器支持。

```javascript

const router = new Router({

mode: 'history',

routes

})

```

服务器配置:

- Nginx:

```nginx

location / {

try_files $uri $uri/ /index.html;

}

```

- Apache:

```apache

RewriteEngine On

RewriteBase /

RewriteRule ^index\.html$ - [L]

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . /index.html [L]

```

九、路由过渡动画

给路由切换添加动画,提升用户体验:

```vue

```

你还可以为不同路由设置不同动画:

```vue

```

十、数据获取

在路由组件中获取数据有两种常见方式:

10.1 导航完成后获取

先导航到新页面,再在组件的`created`钩子中获取数据,适合数据量小的情况:

```javascript

export default {

data() {

return {

user: null,

loading: false

}

},

created() {

// 导航完成后开始获取数据

this.loading = true

this.fetchUser()

},

watch: {

// 监听路由变化,重新获取数据

'$route': 'fetchUser'

},

methods: {

fetchUser() {

this.loading = true

fetch(`/api/user/${this.$route.params.id}`)

.then(res => res.json())

.then(data => {

this.user = data

this.loading = false

})

}

}

}

```

10.2 导航完成前获取

在导航守卫中获取数据,数据加载完成后再导航,适合数据量较大的情况:

```javascript

export default {

data() {

return {

user: null

}

},

beforeRouteEnter(to, from, next) {

// 在导航进入前获取数据

fetch(`/api/user/${to.params.id}`)

.then(res => res.json())

.then(user => {

// 将数据传递给组件实例

next(vm => {

vm.user = user

})

})

},

beforeRouteUpdate(to, from, next) {

// 参数变化时重新获取数据

this.user = null

fetch(`/api/user/${to.params.id}`)

.then(res => res.json())

.then(user => {

this.user = user

next()

})

}

}

```

十一、性能优化

11.1 路由懒加载

像快递分批发货一样,只在需要时才加载路由组件,减小初始加载体积:

```javascript

// 非懒加载

import Home from '@/views/Home.vue'

// 懒加载

const Home = () => import('@/views/Home.vue')

// 路由配置

const routes = [

{

path: '/',

name: 'Home',

component: Home // 现在是懒加载组件

}

]

```

更高级的写法:按组分包,把多个组件打包到同一个文件:

```javascript

const Home = () => import(/ webpackChunkName: "home" / '@/views/Home.vue')

const About = () => import(/ webpackChunkName: "home" / '@/views/About.vue')

```

11.2 组件缓存

使用``缓存路由组件,避免重复渲染:

```vue

```

高级用法:

```vue

```

被缓存的组件会触发`activated`和`deactivated`生命周期钩子:

```javascript

export default {

activated() {

// 组件被激活时调用

console.log('组件被缓存后激活')

},

deactivated() {

// 组件被缓存后失活时调用

console.log('组件被缓存后失活')

}

}

```

十二、常见问题解决

12.1 点击相同路由报错

问题:频繁点击相同导航链接,控制台报错 "Avoided redundant navigation to current location"

解决方案:重写Vue Router的push方法,全局处理错误:

```javascript

// 在router/index.js中

import Router from 'vue-router'

const originalPush = Router.prototype.push

Router.prototype.push = function push(location) {

return originalPush.call(this, location).catch(err => err)

}

```

12.2 路由参数变化页面不更新

问题:从`/user/1`跳转到`/user/2`,页面内容没有更新

原因:Vue Router复用了组件实例,生命周期钩子不会重新执行

解决方案:

- 使用watch监听路由变化(见4.2节)

- 使用导航守卫beforeRouteUpdate(见4.2节)

- 给``添加key:``

12.3 History模式刷新404

问题:History模式下,直接访问二级路由(如`/user/1`)刷新后404

原因:服务器没有正确配置,无法将所有路由指向index.html

解决方案:配置服务器(见8.2节)

12.4 路由守卫无限循环

问题:在全局守卫中使用`next('/login')`导致无限循环

原因:登录页也需要登录验证,形成死循环

解决方案:排除登录页:

```javascript

router.beforeEach((to, from, next) => {

const isLogin = localStorage.getItem('token')

// 如果需要登录且未登录,并且目标不是登录页

if (to.meta.requiresAuth && !isLogin && to.name !== 'Login') {

next({ name: 'Login' })

} else {

next()

}

})

```

12.5 路由动画不生效

问题:添加了过渡动画但没有效果

解决方案:

- 确保``直接包裹``

- 检查动画CSS类名是否正确(与transition的name属性对应)

- 确认路由切换时组件确实被重新渲染(或使用`:key`强制刷新)

十三、新手避坑清单

1. 不要在created/mounted中直接使用`$route.params`:参数变化时不会重新执行这些钩子

2. 使用命名路由代替硬编码路径:`this.$router.push({ name: 'User', params: { id: 1 } })`比`this.$router.push('/user/1')`更易维护

3. params参数不要在path中使用:`router.push({ path: '/user', params: { id: 1 } })`会忽略params

4. History模式需要服务器配置:开发环境没问题,部署到生产环境会404

5. 避免嵌套路由路径重复:子路由path不要以`/`开头,否则会被当作根路由

6. 动态路由参数默认是字符串:需要时记得转换类型(如`Number(this.$route.params.id)`)

7. 路由守卫必须调用next():否则导航会被卡住

8. 不要在beforeRouteEnter中使用this:此时组件实例还未创建

9. keep-alive会缓存组件状态:包含表单输入等临时数据,需要时在activated中重置

10. 路由懒加载不要过度拆分:每个组件一个chunk会导致太多网络请求

十四、实用小技巧

1. 路由元信息:给路由添加额外信息,用于权限控制、页面标题等

```javascript

{

path: '/admin',

component: Admin,

meta: {

requiresAuth: true, // 需要登录

roles: ['admin'], // 角色限制

title: '管理后台' // 页面标题

}

}

```

2. 滚动行为:控制路由切换时页面滚动位置

```javascript

const router = new Router({

mode: 'history',

routes,

scrollBehavior(to, from, savedPosition) {

// 返回savedPosition,在按下 后退/前进 按钮时,会像浏览器的原生表现那样

if (savedPosition) {

return savedPosition

} else {

// 滚动到顶部

return { x: 0, y: 0 }

}

}

})

```

3. 路由别名:给路由设置多个访问路径

```javascript

{

path: '/home',

name: 'Home',

component: Home,

alias: ['/index', '/main'] // 可以通过/index或/main访问Home组件

}

```

4. 路由重定向:将旧路径重定向到新路径

```javascript

{

path: '/old-path',

redirect: '/new-path' // 直接重定向

}

// 或更灵活的函数形式

{

path: '/search',

redirect: to => {

return { path: '/query', query: { q: to.query.keyword } }

}

}

```

5. 动态添加路由:用于权限控制,根据用户角色动态生成路由

```javascript

// 动态添加路由

router.addRoute({

path: '/admin',

component: Admin

})

// 添加子路由

router.addRoute('parentRouteName', {

path: 'child',

component: Child

})

// 删除路由

const removeRoute = router.addRoute(...)

removeRoute() // 调用返回的函数删除路由

```

6. 路由组件复用策略:通过`beforeRouteUpdate`优化数据加载

```javascript

export default {

data() {

return {

userData: null

}

},

async beforeRouteUpdate(to, from) {

// 显示加载状态

this.loading = true

// 取消之前的请求(如果需要)

if (this.cancelToken) {

this.cancelToken.cancel()

}

// 创建新的取消令牌

this.cancelToken = axios.CancelToken.source()

try {

// 获取新数据

this.userData = await getUserData(to.params.id, {

cancelToken: this.cancelToken.token

})

} catch (err) {

if (!axios.isCancel(err)) {

console.error('数据加载失败', err)

}

} finally {

this.loading = false

}

}

}

```

7. 全局路由错误处理:捕获路由跳转错误

```javascript

// 全局路由错误处理

router.onError(error => {

console.error('路由错误:', error)

// 处理懒加载失败等错误

if (error.type === 'missing') {

// 重定向到404页面

router.push('/404')

}

})

```

8. 查询参数格式化:统一处理查询参数的类型转换

```javascript

export default {

computed: {

page() {

// 确保page参数是数字,默认为1

return Number(this.$route.query.page) || 1

},

sort() {

// 限定排序方式

const validSorts = ['asc', 'desc']

return validSorts.includes(this.$route.query.sort)

? this.$route.query.sort

: 'asc'

}

}

}

```

9. 路由切换时保存表单数据:结合keep-alive和localStorage

```javascript

export default {

data() {

return {

formData: {

name: '',

email: ''

}

}

},

activated() {

// 从localStorage恢复数据

const saved = localStorage.getItem('formDraft')

if (saved) {

this.formData = JSON.parse(saved)

}

},

deactivated() {

// 保存到localStorage

localStorage.setItem('formDraft', JSON.stringify(this.formData))

},

beforeUnmount() {

// 组件销毁时清除

localStorage.removeItem('formDraft')

}

}

```

10. 使用路由命名视图:在同一页面展示多个组件

```javascript

// 路由配置

{

path: '/dashboard',

components: {

default: Dashboard,

sidebar: Sidebar,

header: Header

}

}

// 模板中

```

十五、长期使用体验

使用Vue Router开发大型应用一年多后,我总结出以下几点经验:

1. 路由设计要提前规划:特别是嵌套路由结构,后期调整成本高。建议画一张路由结构图,明确父子关系和参数传递方式。

2. 权限控制集中处理:将权限逻辑放在全局守卫中,配合路由元信息,使代码更清晰。例如:

```javascript

// 权限检查函数

function checkPermission(route, userRoles) {

if (!route.meta.roles) return true // 不需要权限

return route.meta.roles.some(role => userRoles.includes(role))

}

// 全局守卫中使用

router.beforeEach((to, from, next) => {

if (to.meta.requiresAuth && !isLogin()) {

next({ name: 'Login' })

} else if (!checkPermission(to, currentUser.roles)) {

next({ name: 'Forbidden' })

} else {

next()

}

})

```

3. 路由参数尽量简单:复杂的状态管理应该交给Vuex/Pinia,而不是通过URL参数传递。URL参数只应用于页面定位,而不是数据存储。

4. 使用TypeScript提升开发体验:为路由和参数定义接口,获得类型提示和编译时检查:

```typescript

interface RouteParams {

id: string

tab?: string

}

export default {

mounted() {

const params: RouteParams = this.$route.params

// 获得类型提示

console.log(params.id)

}

}

```

5. 定期清理路由代码:随着项目迭代,会有很多废弃路由和组件。定期检查并移除不再使用的路由配置,可以减小包体积,提高维护效率。

Vue Router作为Vue生态的核心部分,其设计思想非常符合Vue的渐进式理念。从简单的页面切换到复杂的权限控制,它都能胜任。掌握好路由管理,能让你的单页应用开发事半功倍。

话说回来,路由系统就像