vue-router 3.6 该怎么学?核心用法、实战场景全梳理
前端开发中,单页应用(SPA)的路由管理离不开 vue-router,作为 Vue2 生态的“老伙计”,vue-router 3.6 至今仍是很多项目的选择,但新手常困惑:它咋用?不同场景咋选方案?遇到 bug 咋解决?下面用问答形式,把核心知识点和实战技巧讲透。
vue-router 3.6 是什么?适合哪些前端项目?
vue-router 是 Vue.js 官方的路由管理器,专门解决单页应用的“路径与组件对应”“页面跳转不刷新”“权限控制”等问题。
6 版本属于 Vue2 生态专属(Vue3 要搭配 vue-router 4+),所以如果你在做这些项目,它就是刚需:
- 企业后台系统(如 ERP、OA):需要复杂路由嵌套、权限拦截;
- 营销官网 / 个人博客:需要多页面切换、动态路由(如文章详情页);
- 移动端 H5 应用:需要 Hash 模式兼容低版本浏览器,或 History 模式做 SEO 优化。
简单说,只要是 Vue2 技术栈的 SPA,想让“URL 变化对应组件切换”,vue-router 3.6 就是标配。
从零开始,怎么搭建 vue-router 3.6 的基础路由?
想让路由跑起来,分 4 步:
① 安装依赖
命令行执行:
npm install vue-router@3.6.5 --save
(指定 3.6.5 版本,避免自动装到 4.x 踩坑)
② 配置路由实例(新建 router.js)
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入页面组件(先学基础,暂时不用懒加载)
import Home from './views/Home.vue'
import About from './views/About.vue'
Vue.use(VueRouter) // 注册路由插件
// 定义路由规则:path 对应 URL,component 对应组件
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
// 创建路由实例
const router = new VueRouter({
routes, // 传入规则
mode: 'history' // 路由模式:history(美观但需后端配置)或 hash(带#,兼容性好)
})
export default router
③ 注入到 Vue 根实例(修改 main.js)
import Vue from 'vue'
import App from './App.vue'
import router from './router.js' // 导入路由实例
new Vue({
router, // 注入路由,让所有组件能通过 this.$router / this.$route 访问
render: h => h(App)
}).$mount('#app')
④ 页面中使用路由组件
在 App.vue(或布局组件)里,用 <router-link> 做导航,<router-view> 做组件挂载点:
<template>
<div id="app">
<!-- 导航链接:to 属性指定路径 -->
<router-link to="/">首页</router-link>
<router-link to="/about">lt;/router-link>
<!-- 路由组件的挂载点:匹配到的组件会渲染到这里 -->
<router-view></router-view>
</div>
</template>
现在访问 http://localhost:8080/ 会显示 Home 组件,访问 /about 显示 About 组件,基础路由就跑通了!
动态路由匹配有啥用?怎么处理参数变化?
动态路由URL 里带“可变参数”,比如用户详情页 /user/1、/user/2 共用一个组件。
① 配置动态路由规则
在 router.js 里定义带参数的路径(用 参数名 标记):
{ path: '/user/:id', component: User }
② 组件内获取参数
在 User 组件里,通过 this.$route.params.id 拿到动态参数:
export default {
created() {
console.log('当前用户ID:', this.$route.params.id)
}
}
③ 处理“参数变化但组件复用”的问题
如果从 /user/1 跳转到 /user/2,Vue 会复用 User 组件(因为是同一个组件实例),导致 created 等生命周期不触发,这时候要监听路由变化:
用 watch 监听 $route
export default {
watch: {
'$route'(to, from) {
// to 是目标路由,from 是来源路由
console.log('参数从', from.params.id, '变成', to.params.id)
this.fetchUser(to.params.id) // 重新请求用户数据
}
}
}
用组件内守卫 beforeRouteUpdate
export default {
beforeRouteUpdate(to, from, next) {
this.fetchUser(to.params.id)
next() // 必须调用 next() 继续跳转
}
}
场景举例:电商平台的商品详情页,不同商品 ID 对应同一个组件,需要根据 ID 变化重新请求数据,这时候动态路由+参数监听就派上用场。
嵌套路由怎么实现“页面里套页面”的结构?
很多项目需要“父页面包含子页面”,比如后台管理系统的“侧边栏+主内容区”:点击侧边栏菜单,主内容区切换不同子页面,这时候用嵌套路由。
① 规划路由结构(以后台为例)
父路由:/admin(对应布局组件 AdminLayout,包含侧边栏和子路由出口)
子路由:/admin/posts(文章管理)、/admin/categories(分类管理)
② 配置嵌套路由规则
在 router.js 里,给父路由加 children 数组:
{
path: '/admin',
component: AdminLayout, // 父组件:包含侧边栏和 <router-view>
children: [
{ path: 'posts', component: PostManage }, // 子路由,path 不加 /
{ path: 'categories', component: CategoryManage }
]
}
③ 父组件里放子路由出口
AdminLayout.vue 的模板中,除了侧边栏,还要有 <router-view> 作为子路由的挂载点:
<template>
<div class="admin-layout">
<!-- 侧边栏 -->
<aside>
<router-link to="/admin/posts">文章管理</router-link>
<router-link to="/admin/categories">分类管理</router-link>
</aside>
<!-- 子路由出口:子组件会渲染到这里 -->
<router-view></router-view>
</div>
</template>
现在访问 /admin/posts 时,PostManage 组件会渲染到 AdminLayout 的 <router-view> 里,实现“页面嵌套页面”的效果。
路由传参有哪几种方式?各自适合啥场景?
路由传参有 3 种主流方式,核心差异是参数存储位置和组件解耦程度:
① query 传参:“挂在 URL 问号后”
跳转时用 query 字段,参数会出现在 URL 的 后(如 /search?keyword=Vue)。
跳转方式:
// 字符串路径
this.$router.push('/search?keyword=Vue')
// 对象形式(推荐,配合 name 更可靠)
this.$router.push({
name: 'Search', // 路由规则里的 name(可选,但更稳定)
query: { keyword: 'Vue' }
})
组件内获取:
export default {
created() {
console.log('搜索关键词:', this.$route.query.keyword)
}
}
适合场景:非关键参数、需要“刷新页面后参数保留”“支持 URL 分享”的场景(如搜索页关键词、列表分页)。
② params 传参:“藏在动态路由里”
参数通过动态路由的 参数名 传递,必须在路由规则里定义。
配置路由规则:
{ path: '/user/:id', name: 'User', component: User }
跳转方式:
this.$router.push({
name: 'User', // 必须用 name(用 path 传 params 会丢失)
params: { id: 123 }
})
组件内获取:
export default {
created() {
console.log('用户ID:', this.$route.params.id)
}
}
注意:如果路由规则没定义 id,直接用 params 传参会导致参数丢失(刷新页面后 params 也会消失)。
适合场景:路径必须包含的“关键标识”(如用户 ID、文章 ID),且不需要 URL 显式暴露参数(但动态路由的参数其实会显式在 URL 里)。
③ props 传参:“让组件更纯”
把路由参数当作组件的 props 传递,减少组件对 $route 的依赖,更易复用和测试。
配置路由规则:在路由里开启 props: true(或函数形式自定义传参):
{ path: '/user/:id', component: User, props: true }
组件内接收:用 props 声明参数:
export default {
props: ['id'], // 直接接收路由参数作为 props
created() {
console.log('用户ID:', this.id)
}
}
适合场景:需要“组件解耦”的场景(比如组件要做单元测试,不想依赖 $route),或参数需要二次处理(用函数形式传参)。
搜索页用 query(方便分享),详情页用 params(路径必须带 ID),公共组件用 props(解耦好维护)。
导航守卫怎么控制页面权限和跳转逻辑?
导航守卫是“路由跳转的生命周期钩子”,能在跳转前、跳转中、跳转后做逻辑处理(比如权限判断、数据预加载),分 3 类:
① 全局守卫:router.beforeEach(全局生效)
在 router.js 里配置,对所有路由跳转生效。典型场景:登录拦截。
示例:拦截需要登录的页面
router.beforeEach((to, from, next) => {
// to:目标路由;from:来源路由;next:必须调用才会继续跳转
const requiresAuth = to.meta.requiresAuth // 路由元信息标记是否需要登录
const isLogin = localStorage.getItem('token') // 假设用 localStorage 存 token
if (requiresAuth && !isLogin) {
next('/login') // 没登录,跳转到登录页
} else {
next() // 允许跳转
}
})
然后在需要登录的路由里加 meta:
{
path: '/admin',
component: AdminLayout,
meta: { requiresAuth: true } // 标记需要登录
}
② 路由独享守卫:beforeEnter(只对当前路由生效)
在单个路由规则里配置,仅对当前路由的跳转生效。典型场景:付费内容拦截。
示例:拦截未购买的付费课程页面
{
path: '/course/premium',
component: PremiumCourse,
beforeEnter: (to, from, next) => {
const isPurchased = localStorage.getItem('purchased')
if (!isPurchased) {
next('/course/buy') // 没购买,跳转到购买页
} else {
next()
}
}
}
③ 组件内守卫:beforeRouteEnter / beforeRouteUpdate / beforeRouteLeave
在组件内部定义,对当前组件的路由跳转生效。典型场景:表单离开提示、参数变化处理。
示例:离开编辑页时提示保存
export default {
data() {
return { formChanged: false }
},
beforeRouteLeave(to, from, next) {
if (this.formChanged) {
const confirm = window.confirm('表单有修改,是否保存?')
if (confirm) {
this.saveForm()
next()
} else {
next(false) // 取消跳转
}
} else {
next()
}
}
}
导航守卫的核心是控制跳转流程,通过 next() 的参数(如 next('/login')、next(false))决定是否允许跳转、跳转到哪。
路由懒加载怎么优化项目性能?
传统路由写法是“先 import 所有组件,再打包”,会导致首屏加载一个超大 JS 文件。路由懒加载能把组件分成多个小文件,访问对应路径时再加载,减少首屏压力。
① 懒加载写法:用动态 import()
把 router.js 里的组件导入改成动态导入:
const Home = () => import('./views/Home.vue')
const About = () => import('./views/About.vue')
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
这样打包后,Home 和 About 会变成两个独立的 JS 文件(chunk),只有访问对应路径时才会加载。
② 进阶:给 chunk 命名(webpack 魔法注释)
为了方便调试,给每个 chunk 起名字:
const Home = () => import(/* webpackChunkName: "home" */ './views/Home.vue') const About = () => import(/* webpackChunkName: "about" */ './views/About.vue')
打包后,文件会变成 home.js、about.js,更易识别。
适合场景:页面多、组件体积大的项目(如后台系统有几十个页面),首屏只加载首页,其他页面按需加载,能大幅减少首屏加载时间。
vue-router 3.6 和 Vue3 能一起用吗?是否要升级到 4.x?
一句话总结版本对应:
- vue-router 3.x → 只能搭配 Vue2;
- vue-router 4.x → 只能搭配 Vue3。
- 如果你项目是 Vue2 技术栈,继续用 3.6 完全没问题,生态成熟、文档丰富;
- 如果你要升级到 Vue3,必须把 vue-router 也升级到 4.x,同时要改代码:
| 功能 | vue-router 3.x | vue-router 4.x |
|---|---|---|
| 路由实例创建 | new VueRouter({ routes }) |
createRouter({ history, routes }) |
| 路由模式 | mode: 'history' |
history: createWebHistory() |
| 组件内使用 | this.$router / this.$route |
useRouter() / useRoute()(组合式 API) |
升级建议:小项目可直接升级 Vue3 + vue-router 4.x;大项目要评估改动成本,可先做技术调研再推进。
路由开发中常见问题怎么解决?
开发时遇到路由不生效、跳转报错、参数丢了…别慌,按场景排查:
① 路由不生效:页面一直空白
- 检查
router是否注入到 Vue 根实例(main.js里有没有router选项); - 检查
routes配置:path有没有拼写错,component路径是否正确(特别是懒加载时的路径); - 检查
<router-view>是否在App.vue或父组件里正确放置(没放的话,匹配的组件没地方渲染)。
② 跳转报错:NavigationDuplicated
6 版本重复跳转到同一路径(如连续点击同一个 <router-link>)会报错,解决方法:全局捕获错误。
在 router.js 里加这段代码:
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
③ 参数获取不到:$route.params / $route.query 是空的
- 如果是
params:检查路由规则是否定义了动态段(如id),且跳转时用了name(用path传params会丢失); - 如果是
query:检查跳转时是否用了query字段,且 URL 里确实有?xxx=yyy。
④ 嵌套路由不显示:子
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


