Vue3路由跳转怎么玩才不出错?新手进阶都要注意的全场景技巧
你好呀,不管你是刚接触Vue3想搭个小博客练手,还是在维护企业级项目遇到路由跳转乱码、传参丢值的坑,今天这篇文章都能帮你捋清楚,首先得明确一点,现在Vue生态下官方推荐的路由库是Vue Router 4.x,别再用3.x的老路子往Vue3里套啦,API简化了不少,但也有几个新坑容易踩,接下来我们分场景慢慢说,从最基础的入门操作,到带参数、带状态的进阶用法,再到项目里常用的导航拦截和缓存控制,最后补几个避坑指南,都是我平时写项目攒的实战经验。
从入门开始先搞懂Vue3路由跳转的两种核心方式
入门同学接触最多的肯定是跳转方式,Vue Router 4.x里有两种官方推荐的:一种是声明式跳转,写在模板里的;另一种是编程式跳转,写在JS逻辑里的,两种方式各有适用场景,先别着急背API,得搞懂什么时候用哪个。
先聊聊声明式跳转吧,这种方式最省心,不用写一行JS代码,直接用Vue Router提供的<router-link>组件就行,很多新手刚上手会把它当成<a>标签用,但其实完全不一样——<a>标签是浏览器原生跳转,会刷新整个页面,导致Vue的响应式数据、组件状态全清零;而<router-link>组件是客户端路由跳转,只会替换页面里挂载<router-view>的那部分内容,页面不会闪一下,体验好太多,那它怎么用呢?最基础的就是绑定to属性,属性值可以是字符串路径,比如你有个首页路径叫/home,直接写<router-link to="/home">回首页</router-link>就行;也可以是对象,这个后面讲传参的时候会详细说。
接下来是编程式跳转,这个就灵活多了,所有需要根据逻辑判断才能跳转的场景都得用它——比如用户登录成功后跳转到个人中心,或者搜索框输入关键词回车后跳转到搜索结果页,在Vue3里,编程式跳转不能再像Vue2那样直接用this.$router.push了,因为Vue3的组件里this指向变了(setup语法糖里甚至没有this),所以得先导入路由实例才行,有两种导入方式:一种是在setup语法糖外部的选项式API里用useRouter()组合式函数获取实例,另一种是直接在setup里调用,举个选项式的小例子:先在组件的<script>里导入useRouter,然后在created或者methods里调用const router = useRouter(),最后在点击事件里用router.push('/personal-center');如果是setup语法糖就更简单了,直接写在<script setup>的开头:import { useRouter } from 'vue-router',然后定义个事件函数,比如const goSearch = () => { router.push({ name: 'Search', params: { keyword: searchInput } }) },这里要注意,编程式跳转除了push还有replace、go、back、forward,它们的区别也得记牢:push是往浏览器历史记录里加一条新的,用户点返回键能回到上一页;replace是替换当前的历史记录,用户点返回键会跳到更前面的;go是传数字,正数前进负数后退,比如go(-2)就是后退两页;back就是go(-1),forward就是go(1),这三个是浏览器原生的历史操作封装。
全场景参数传递:params、query、state到底用哪个?
说完了跳转方式,接下来就是新手最常遇到的问题:跳转的时候要带数据怎么办?别慌,Vue Router 4.x给了三种常用的方式,每种都有自己的适用场景,乱选的话要么参数丢要么URL丑要么SEO差,下面一个个对比给你看。
第一种是params参数,这个参数是放在路由路径里的,不会直接显示在URL的问号后面,但前提是你要先在路由配置里给路径加上占位符——比如你要做个文章详情页,需要传文章ID,那路由配置就得写成{ path: '/article/:id', name: 'ArticleDetail', component: ArticleDetail },这里的id就是占位符,还能加个问号变成可选参数,比如/article/:id?,没传ID的时候也能跳,那怎么传呢?声明式的话对象形式写<router-link :to="{ name: 'ArticleDetail', params: { id: 123 } }">看文章123</router-link>;编程式的话同样用对象,router.push({ name: 'ArticleDetail', params: { id: 123 } })。敲黑板!这里有个新手巨容易踩的坑:用params传参的时候,必须搭配路由的name属性,不能只用path!因为Vue Router 4.x默认不允许用path加params的组合(虽然某些旧版本浏览器或者极端配置下可能行,但官方文档明确说了不推荐,而且会有兼容性问题),那怎么接收呢?在目标组件里用useRoute()组合式函数获取当前路由信息,然后route.params.id就能拿到了,params参数还有个小特点:刷新页面后参数还在,因为它是路由路径的一部分,但如果跳转的时候没传占位符对应的参数,而且没加问号设成可选,页面就会直接报404。
第二种是query参数,这个参数大家应该很熟悉,就是URL问号后面的键值对,比如/article?id=123&author=xiaoming,query参数的优点是不用在路由配置里提前写占位符,搭配name或者path都能用,而且刷新页面后参数也不会丢,SEO友好(搜索引擎会抓取问号后面的参数内容);缺点是URL会变得比较长,而且参数是明文显示在地址栏里的,不能传敏感信息(比如密码、token这种,千万不要用query传!),传参的话更简单,声明式用对象<router-link :to="{ path: '/article', query: { id: 123, author: 'xiaoming' } }">看小明的文章123</router-link>,编程式也是对象router.push({ path: '/article', query: { id: 123, author: 'xiaoming' } });接收的话同样用useRoute(),route.query.id或者route.query.author就行,这里要注意,不管是params还是query,接收到的参数默认都是字符串类型的,如果传的是数字或者布尔值,记得要自己转换一下,比如Number(route.params.id),不然用做判断的时候会出错。
第三种是state参数,这个是Vue Router 4.x新增的,搭配HTML5的History API用的(Hash模式下也能用,但Hash模式本来就是靠URL的hash值模拟路由的,state参数是存在浏览器的历史记录栈里的,Hash模式下也不会受影响),state参数的优点是完全不显示在URL里,刷新页面后参数还在(不过如果用户手动输入URL跳转到这个页面,或者从其他网站跳过来,state参数就会丢失),而且能传任意类型的数据(比如对象、数组、函数都可以,不过函数传过去刷新页面就会变成undefined,尽量别传函数);缺点是不能在路由配置里提前约束,也不能直接通过URL分享带state参数的页面,传参的话,不管是声明式还是编程式,都是在to对象里加state属性:声明式<router-link :to="{ name: 'OrderConfirm', state: { cartList: myCart, totalPrice: 999 } }">去确认订单</router-link>,编程式router.push({ name: 'OrderConfirm', state: { cartList: myCart, totalPrice: 999 } });接收的话稍微不一样一点,虽然useRoute()里也有state属性,但更推荐直接用浏览器原生的history.state(或者组合式API里的useRoute().state也一样),而且这里接收到的数据类型不会变,传的是数字就是数字,传的是对象就是对象,不用转换,这种参数特别适合传那种临时的、大的、不想让别人看到的、不需要分享的数据,比如购物车列表、表单填写的草稿。
进阶必学:导航守卫控制跳转,让你的路由更安全
入门操作和传参都搞懂了,接下来就得学进阶的了——导航守卫,什么是导航守卫?简单来说就是“路由跳转前/后要做的事”,比如用户没登录不能跳转到个人中心,用户要离开表单页但还没保存数据要弹出提示,或者跳转前要先加载页面需要的数据,Vue Router 4.x的导航守卫分为全局守卫、路由独享守卫、组件内守卫三种,每种都有不同的触发时机,得根据场景选合适的。
先讲全局守卫,全局守卫顾名思义就是对整个项目的所有路由跳转都生效,写在路由配置文件里(就是router/index.js或者router/index.ts),全局守卫有三个:beforeEach、beforeResolve、afterEach。beforeEach是最常用的,触发时机是“任何路由跳转开始之前”,所有的权限控制都可以写在这里——比如判断用户有没有token,没有token的话就跳转到登录页,而且要把当前要跳转的路径存下来,等用户登录成功后直接跳回去,提升用户体验,举个小例子:先导入useStore(如果用了Pinia或者Vuex存token的话),然后在createRouter之后、export default router之前写router.beforeEach((to, from, next) => { /* 这里写逻辑 */ }),这里要注意,Vue Router 4.x的beforeEach可以不用显式调用next(),直接返回一个值就行:返回true或者undefined就是允许跳转,返回一个路由路径(字符串或者对象)就是重定向到那个路径,返回false就是取消跳转,这个变化比Vue2好太多了,不容易出现next()调用次数不对的问题。beforeResolve的触发时机是“所有组件内守卫和异步路由组件解析之后,跳转确认之前”,用得比较少,一般用来处理需要在跳转前确保所有异步资源都加载完成的场景。afterEach的触发时机是“路由跳转完成之后”,没有next(),也不能取消跳转或者重定向,一般用来改页面的标题、埋点统计这些。
然后是路由独享守卫,这个守卫只对单个路由生效,写在路由配置对象里,路由独享守卫只有一个:beforeEnter,触发时机和beforeEach类似,也是“该路由跳转开始之前”,但只针对这一个路由,比如你有个管理员后台的路由,只有登录且角色是admin的用户才能进,就可以把权限控制的逻辑单独写在这个路由的beforeEnter里,不用都堆在全局守卫里,代码更清晰。beforeEnter的用法和beforeEach差不多,也可以直接返回值,不用next()。
组件内守卫,这个守卫是写在组件里的,只对当前这个组件的路由跳转生效,组件内守卫有三个:onBeforeRouteEnter、onBeforeRouteUpdate、onBeforeRouteLeave,这三个都是组合式函数,必须写在setup里(或者用onActivated配合Vue2的组件内守卫也能在选项式API里用,但组合式更方便)。onBeforeRouteEnter的触发时机是“该组件被路由激活之前”,注意哦,这时候组件还没挂载,setup还没执行完,所以拿不到组件的实例(如果是选项式API的beforeRouteEnter,可以通过next(vm => { /* 这里vm是组件实例 */ })拿到,但组合式的没有next(vm)这种用法,所以如果要操作组件实例,只能用nextTick或者onMounted)。onBeforeRouteUpdate的触发时机是“该组件被复用的时候”——什么是组件复用?比如你从/article/123跳转到/article/456,两个路由用的都是同一个ArticleDetail组件,这时候组件不会销毁再重建,而是复用,所以created、mounted这些生命周期钩子都不会触发,这时候就要用onBeforeRouteUpdate来重新加载新的文章数据。onBeforeRouteLeave的触发时机是“离开该组件的路由跳转开始之前”,这个也很常用,比如用户要离开表单页但还没保存,就可以弹出一个确认框:“您的内容还没保存,确定要离开吗?”,如果用户点确定就返回true,点取消就返回false。
避坑指南:这些Vue3路由跳转的坑你踩过吗?
前面讲了这么多用法,最后补几个我平时写项目踩过的、还有身边朋友经常问的坑,帮你少走弯路。
第一个坑:Hash模式和History模式的区别,还有History模式下的404问题,很多新手刚搭项目的时候不知道选什么模式,直接用默认的Hash模式,Hash模式的URL里会有个,比如http://localhost:3000/#/home,虽然看起来丑,但好处是不用后端配合,刷新页面不会报404,适合个人项目、测试环境或者不能改后端配置的项目;History模式的URL是干净的,比如http://localhost:3000/home,SEO友好,适合正式上线的项目,但必须后端配合,在服务器上配置重定向规则,把所有的路由请求都重定向到index.html,不然用户刷新页面就会报404,怎么配置服务器的重定向规则呢?不同的服务器配置不一样,比如Nginx的话可以在配置文件里加try_files $uri $uri/ /index.html;,Apache的话可以用.htaccess文件,具体的可以去查官方文档或者搜索引擎,这里就不细说了。
第二个坑:用params传参必须搭配name,不能只用path,这个前面已经提过了,但还是要再强调一遍,因为实在太容易踩了!如果只用path加params,要么跳转失败,要么params参数会被忽略,要么刷新页面后参数丢失,而且官方文档明确说了不推荐这种用法,所以一定要养成用name加params的习惯。
第三个坑:参数类型的转换,不管是params还是query,接收到的默认都是字符串类型的,比如传的是id: 123,接收到的是"123",如果用route.params.id === 123做判断,结果肯定是false,所以记得要自己转换一下,比如Number(route.params.id)或者Boolean(route.query.isVip)。
第四个坑:异步路由组件的懒加载,很多企业级项目为了提升首屏加载速度,都会把路由组件改成懒加载,也就是只有跳转到这个路由的时候才会加载对应的JS文件,Vue Router 4.x的懒加载写法和Vue2差不多,但稍微简化了一点:比如原来Vue2是component: () => import('./views/Home.vue'),Vue3还是这个写法,但要注意,不能在import里加变量,比如() => import(./views/${pageName}.vue,这种写法会导致打包工具(比如Vite或者Webpack)无法正确识别模块,懒加载失效,所以每个路由组件的import都要写死。
第五个坑:路由跳转后滚动条的位置,很多新手搭项目的时候会发现,从首页跳转到文章详情页,滚动条的位置还停留在首页的下方,用户体验很差,这个问题怎么解决呢?可以在路由配置文件里加一个scrollBehavior函数,这个函数的触发时机是“路由跳转完成之后”,可以返回一个滚动位置的对象,比如{ top: 0 }就是每次跳转后滚动到顶部;如果要恢复上次离开这个页面时的滚动位置,可以返回savedPosition,这个是Vue Router 4.x自动保存的,举个小例子:const router = createRouter({ history: createWebHistory(), routes, scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { top: 0 } } } })。
好了,今天关于Vue3路由跳转的全场景技巧就讲到这里了,从入门的两种跳转方式,到进阶的三种参数传递、三种导航守卫,再到五个常见的避坑指南,都是实战经验总结,应该能帮你解决大部分问题,如果还有其他问题,可以在评论区留言哦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



