一、最基础的路由跳转,声明式与编程式导航的区别
在 Vue2 开发单页面应用时,路由跳转是串联页面、实现交互逻辑的关键环节,不管是点击导航栏切换页面,还是根据用户操作动态跳转到指定路径,都得靠路由跳转来实现,但刚接触 Vue2 + vue-router 的同学,往往会疑惑「各种场景下怎么选跳转方式?传参、嵌套路由这些复杂情况咋处理?」今天就从基础到进阶,把 Vue2 路由跳转的门道拆明白。
Vue2 里实现路由跳转,核心有两种方式:声明式导航(用 <router-link>
组件)和 编程式导航(用 this.$router
里的方法),先搞清楚它们的区别,才能选对场景。
声明式导航:<router-link>
怎么用?
你可以把 <router-link>
理解成“能跳转的 <a>
标签”,但它不会像普通 <a>
那样刷新页面(毕竟是单页应用),基本用法是给 to
属性传目标路径,
<router-link to="/home">首页</router-link> <router-link to="/about">关于我们</router-link>
如果要传更复杂的配置(比如命名路由、带参数),to
还能写成对象形式:
<!-- 假设路由配置里有 name: 'user' --> <router-link :to="{ name: 'user', params: { id: 123 }}">用户123</router-link>
这种方式适合静态导航场景,比如页面顶部的导航栏、侧边菜单,用户点一下就跳转,逻辑简单直接。
编程式导航:this.$router.push
啥时候用?
编程式导航更灵活,适合需要逻辑判断后再跳转的场景(比如登录成功后跳转到首页、表单提交后跳转到结果页),核心方法是 this.$router.push()
,它的作用是“往路由历史记录里添加一条新记录”,用法和 <router-link>
的 to
类似,能传字符串路径或对象:
// 传字符串路径 this.$router.push('/home') // 传对象(命名路由 + 参数) this.$router.push({ name: 'user', params: { id: 123 } })
除了 push
,还有 replace
和 go
两个常用方法,后面会详细讲它们的区别~
带参数跳转:query 和 params 该怎么选?
做项目时,经常需要“跳转到详情页并传 ID”“搜索页带关键词跳转”这类需求,这时候得用路由传参,而传参又分 query
和 params
两种方式,它们的区别和场景很关键。
query 传参:把参数“挂在 URL 上”
query
传参的逻辑类似 HTTP 的 GET 请求,参数会直接拼在 URL 后面(/user?id=123
),用法是在跳转时给 query
字段传对象:
// 声明式(<router-link>) <router-link :to="{ path: '/user', query: { id: 123 }}">用户123</router-link> // 编程式(this.$router.push) this.$router.push({ path: '/user', query: { id: 123 } })
特点:参数暴露在 URL 里,刷新页面后参数不会丢失;适合需要“分享链接带参数”“刷新后参数保留”的场景(比如搜索结果页,用户刷新后还能看到关键词)。
params 传参:把参数“藏在路由里”
params
传参需要配合命名路由和动态路由匹配(路由配置里写 /user/:id
),先看路由配置:
const router = new VueRouter({ routes: [ { name: 'user', // 命名路由 path: '/user/:id', // 动态段 :id component: User } ] })
然后跳转时,给 params
传对象:
// 声明式 <router-link :to="{ name: 'user', params: { id: 123 }}">用户123</router-link> // 编程式 this.$router.push({ name: 'user', params: { id: 123 } })
这时候 URL 会变成 /user/123
,参数“藏”在路径里,看起来更简洁,但要注意:如果用 path
而不是 name
跳转,params
会被忽略(因为 path
是硬编码的路径,无法和动态段匹配)。
特点:参数不在 URL 上明文显示(但其实动态段还是能看到),适合“参数不需要暴露、但希望 URL 更简洁”的场景;但刷新页面后参数会丢失(因为 params
本质是“路由组件的 props”,不是 URL 的一部分),如果想刷新不丢参数,要么改用 query
,要么把参数写进路由的 path
里(像上面的 /user/:id
这样)。
编程式导航的更多技巧:push、replace、go 怎么玩?
前面讲了 push
,但 replace
和 go
也有各自的妙用,掌握它们能解决很多“跳转后回退”的问题。
push:添加新的历史记录
this.$router.push()
会往路由历史栈里新增一条记录,所以用户点浏览器的“返回”按钮,能回到上一个页面,比如从首页跳转到详情页,用 push
的话,返回能回到首页,这是最常用的跳转方式。
replace:替换当前历史记录
this.$router.replace()
会替换当前的历史记录,而不是新增,比如用户完成支付后跳转到“支付成功页”,这时候用 replace
,用户点返回就不会回到“支付页”(避免重复支付),用法和 push
一样,只是把方法名换成 replace
:
this.$router.replace('/pay-success')
go:控制前进/后退步数
this.$router.go(n)
类似浏览器的 history.go(n)
,n
是数字,表示“前进/后退多少步”。
this.$router.go(-1)
:返回上一页(等同于浏览器的“返回”按钮)this.$router.go(1)
:前进到下一页(如果历史记录里有)
实际开发中,“返回按钮”功能常用 go(-1)
实现,比如在页面右上角加个返回按钮:
<button @click="$router.go(-1)">返回</button>
嵌套路由场景下的跳转逻辑
很多项目会用“嵌套路由”(比如布局页里嵌套首页、个人中心),这时候跳转要注意路径的层级关系,先看一个嵌套路由的配置示例:
const router = new VueRouter({ routes: [ { path: '/layout', component: Layout, // 父组件 children: [ { path: 'home', component: Home }, // 子路由:/layout/home { path: 'profile', component: Profile } // 子路由:/layout/profile ] } ] })
绝对路径 vs 相对路径
跳转嵌套路由时,路径可以用绝对路径(以 开头,/layout/home
),也可以用相对路径(不以 开头,home
)。
- 绝对路径:适合在任意组件里跳转,路径写死,
<router-link to="/layout/home">首页</router-link>
。 - 相对路径:适合在父路由组件(Layout)里跳转,路径基于当前路由的路径,比如在 Layout 组件里,写
<router-link to="home">首页</router-link>
,它会自动拼成/layout/home
,这样修改父路由的path
时,子路由跳转不用跟着改,更灵活。
编程式跳转的小技巧
在父组件(Layout)里用编程式导航时,相对路径同样生效:
// Layout 组件里的方法 goToHome() { this.$router.push('home') // 等同于 push('/layout/home') }
如果是在子组件(Home)里跳转到另一个子组件(Profile),用相对路径的话要注意层级,this.$router.push('../profile')
(但这种情况少用,一般直接用绝对路径更清晰)。
路由守卫里的跳转:控制权限与流程
路由守卫是“跳转前/后拦截逻辑”,最常用的是全局前置守卫(beforeEach),可以用来做权限控制(比如未登录用户不能进个人中心)。
全局前置守卫:beforeEach
在路由配置文件里,给 router
加 beforeEach
钩子:
router.beforeEach((to, from, next) => { // to:要跳转到的目标路由 // from:当前离开的路由 // next:必须调用的函数,决定是否跳转 if (to.meta.requiresAuth && !isLogin()) { // 如果目标路由需要授权,且用户未登录,跳转到登录页 next({ name: 'login' }) } else { // 否则放行 next() } })
这里要注意:next
只能调用一次,否则会报错,如果在守卫里跳转(比如跳转到登录页),要避免“无限循环”——比如登录页也需要 requiresAuth
,那逻辑要改成:
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !isLogin()) { if (to.name === 'login') { // 如果已经在登录页,说明用户没权限,留在登录页 next(false) } else { next({ name: 'login' }) } } else { next() } })
组件内守卫:beforeRouteEnter
组件内的守卫(beforeRouteEnter
)能在“组件进入前”做逻辑,但要注意,beforeRouteEnter
执行时,组件实例(this
)还没创建,所以如果要在跳转前操作组件数据,得用 next(vm => { ... })
:
export default { beforeRouteEnter(to, from, next) { // 这里 this 是 undefined next(vm => { // vm 是组件实例,可以操作 vm.data vm.fetchData() // 进入组件后调用获取数据的方法 }) } }
常见跳转问题的排查与解决
实际开发中,路由跳转容易碰到“页面不更新”“参数丢失”“跳转失败”这些坑,这里总结几个高频问题的解法。
跳转后页面不更新:组件复用导致
比如从 /user/1
跳转到 /user/2
,路由参数变了,但组件实例没销毁(Vue 为了性能复用组件),所以组件的 created
、mounted
不会重新执行,页面也就不更新。
解法 1:监听 $route
的变化,在 watch
里处理逻辑:
export default { watch: { '$route'(to, from) { // 路由变化时,重新获取数据 this.fetchData(to.params.id) } } }
解法 2:给 <router-view>
加 key
,强制组件销毁重建:
<router-view :key="$route.fullPath"></router-view>
params 传参后刷新页面,参数丢失
前面讲过,params
传参如果没把参数写进路由的 path
(/user/:id
),刷新时参数会丢失(因为 params
不是 URL 的一部分)。
解法:要么改用 query
传参(参数挂在 URL 上,刷新不丢),要么把参数配置成动态路由(/user/:id
),这样参数会被包含在 URL 里,刷新也能保留。
编程式跳转没反应:检查路由配置和 this 指向
this.$router.push()
没效果,先检查这两点:
- 路由配置是否正确:目标路由的
name
、path
有没有拼写错误?有没有在routes
里配置? this
是否指向 Vue 实例:如果用箭头函数定义方法,this
会丢失,要改成普通函数:
// 错误:箭头函数导致 this 不是 Vue 实例 methods: { goToPage: () => { this.$router.push('/home') // this 是 undefined } } // 正确:普通函数 methods: { goToPage() { this.$router.push('/home') } }
按场景选对方法,跳转更丝滑
Vue2 路由跳转的核心是选对声明式/编程式,分清 query/params,处理好嵌套和守卫,最后再梳理下不同场景的选择逻辑:
- 静态导航(菜单、Tab)→ 用
<router-link>
声明式; - 逻辑判断后跳转(登录、表单提交)→ 用
this.$router.push/replace
编程式; - 带参数且需分享/刷新保留 → 用
query
; - 带参数且想 URL 简洁 → 用
params
+ 动态路由; - 嵌套路由跳转 → 优先用相对路径,减少硬编码;
- 权限控制 → 用全局守卫
beforeEach
拦截;
把这些逻辑理顺,再遇到路由跳转的需求,就能快速选对方法,少踩坑啦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。