uni-app 用 Vue3 怎么做路由守卫:3 种拦截方式的取舍

编程狮(w3cschool.cn) 2026-10-29 07:06:24 浏览数 (33)
反馈

uni-app 用 Vue3 做路由守卫,核心是在「页面跳转前」插入一段拦截逻辑,决定放行、重定向还是拒绝。和纯网页的 vue-router 不同,uni-app 跑在多端,没有浏览器的 history 概念,所以守卫要落在它自己的路由 API 上。本文用「未登录跳转登录页」这个最小场景,讲清前置守卫、页面内拦截、以及全局拦截三种方式的配置与取舍,并给出能直接复现、运行即可验证的代码。适用范围:uni-app 的 Vue3(vite)工程;具体 API 随引擎版本变化,以官方文档为准,未公开行为均标注为推断。

uni-app 用 Vue3怎么做路由守卫

关键结论先给出:拦截逻辑放在「跳转发起处」最直观,放在「页面加载处」最稳,放在「全局拦截器」最适合跨端统一规则。三种方式不是互斥的,通常组合使用——用全局拦截兜住通用规则,用页面内拦截处理局部逻辑。

一、先把 uni-app 路由和网页路由的差异看清(适用与边界)

uni-app 的跳转靠 uni.navigateTo / uni.redirectTo 这类 API,而不是 <a> 或 router.push。这意味着你熟悉的 vue-router 前置守卫在这里并不天然可用,得按 uni-app 的机制来。边界要记牢:小程序端没有真正的 URL 地址栏,所谓「路由」是框架维护的页面栈,守卫只能挂在跳转动作或页面生命周期上。

先看一份最朴素、没有任何守卫的跳转:

// 某个按钮点击后
uni.navigateTo({ url: '/pages/detail/detail?id=1' })

这种写法点哪跳哪,没法拦。要让它在「未登录」时改道,就得把判断加进跳转前。uni-app 中文文档里对页面栈与跳转 API 有完整说明,是后续一切的地基。

版本差异:Vue3 工程里推荐用 setup 语法和组合式 API,守卫代码建议写在 onLoad 或独立的导航工具函数里,不要散落在各个页面。具体目录约定以你所用脚手架为准(推断)。

二、方式一:跳转前在发起处拦截(介绍与对比)

最直观的做法,是把判断包在跳转函数里:不满足就跳转登录页,满足才跳目标页。逻辑集中、好读。

function goDetail(id) {
  const token = uni.getStorageSync('token')
  if (!token) {
    uni.redirectTo({ url: '/pages/login/login' })
    return
  }
  uni.navigateTo({ url: `/pages/detail/detail?id=${id}` })
}

对比三种方式:发起处拦截「改起来快、但每个入口都得写一遍」,页面内拦截「只写一次、但要在每个目标页加代码」,全局拦截「写一次管全部、但定制性差」。小项目用发起处拦截最省事,中大型项目通常升级到全局拦截。

这种方式的代价是重复:十个入口就要写十次判断。若你用的是Vue3 教程里的组合式写法,可以把这段抽成一个 navigate 封装,让所有页面统一走它,重复问题就解决了。

三、方式二:在目标页 onLoad 里拦截(验证与输出)

另一种思路是不动跳转处,而是在目标页加载时判断:没权限就立刻重定向走。这样即使有人绕过封装直接跳,也会被拦下,更稳。

import { onLoad } from '@dcloudio/uni-app'
onLoad(() => {
  const token = uni.getStorageSync('token')
  if (!token) {
    uni.redirectTo({ url: '/pages/login/login' })
  }
})

预期结果(未在本机执行):未登录用户直接访问受保护页面时,会立刻被弹回登录页;已登录则正常显示。验证时若发现重定向后又闪一下原页面,通常是 onLoad 里先渲染了内容,修复方式是把判断放在渲染之前、或加一个加载中的占位。

这种「页面内拦截」的好处是兜底严,坏处是每个受保护页都要写。中大型项目把它和全局拦截搭配,能兼顾严与省。需要排错时,Apicloud Junior Develop Guide 教程里关于生命周期顺序的说明也值得对照理解。

发起处拦截与页面内拦截对比

四、方式三:全局拦截器统一兜规则(对比与取舍)

uni-app 提供了 uni.addInterceptor 能拦截几乎所有路由 API,适合把「登录校验」这种通用规则写一次、管全部。这是三种里最省维护的。

uni.addInterceptor('navigateTo', {
  invoke(args) {
    const token = uni.getStorageSync('token')
    if (!token && args.url.startsWith('/pages/protected')) {
      uni.redirectTo({ url: '/pages/login/login' })
      return false   // 中断本次跳转
    }
  }
})

全局拦截的优点是集中、不会漏;代价是定制性弱——复杂的条件很难塞在一个拦截器里。所以实战里常见组合:全局拦截兜「是否登录」这种硬规则,页面内拦截处理「这个页面特有的权限」。注意拦截器返回 false 才能中断,返回 undefined 不会阻止跳转。

全局拦截器在路由调用链上的插入位置

五、常见失败与修复(失败、边界、排错)

坑一,拦截器忘了返回 false。表现是不报错但跳转照常发生,修复方式是在 invoke 里返回 false 显式中断。

坑二,重定向死循环。登录页本身也被守卫拦截,结果在登录页和受保护页之间来回跳。修复方式是给登录页 URL 加白名单,守卫里跳过它。

坑三,多端行为不一致。小程序端某些 API 与 H5 端有差异,修复方式是用条件编译或统一走 uni 前缀 API,不要混用平台特有写法。需要画矢量图标时可查SVG 速查手册。

坑四,判断写在渲染之后。表现是先闪原页面再跳走,修复方式是把拦截提到最前、用加载占位遮住。

总结

uni-app 用 Vue3 做路由守卫有三种落点:跳转发起处拦截最直观但易重复,目标页 onLoad 拦截最稳但需每页写,全局 addInterceptor 最省维护但定制性弱。实战里通常组合——全局拦截兜登录这类硬规则,页面内拦截处理局部权限,发起处封装统一入口。无论哪种,都要记得给登录页加白名单避免重定向死循环,并在拦截里显式返回 false 才能真正中断跳转。

常见问题

Q:uni-app 里能用 vue-router 的前置守卫吗?

不能直接用。uni-app 跑在多端、没有浏览器 history,跳转靠 uni.navigateTo 这类 API,守卫要落在拦截器或页面生命周期上,而不是 vue-router 的 beforeEach。

Q:全局拦截器为什么没拦住跳转?

最常见是 invoke 里没有返回 false。不返回或返回 undefined 都不会中断本次跳转,必须在条件成立时显式 return false。

Q:为什么会出现登录页重定向死循环?

因为守卫把登录页本身也判为「未登录需拦截」,于是不断在登录页和受保护页之间互跳。修复方式是在守卫里给登录页 URL 加白名单、跳过校验。

Q:拦截器里能拿到跳转参数做精细化判断吗?

能。invoke 的 args 里带着目标页 URL 与参数,可先解析再决定放行或重定向;例如在需要登录的页面上只放行带有效 token 的进入,其余统一弹登录。注意参数来源要可信,别直接拿参数当权限证据,避免被伪造 URL 绕过。

延伸学习

0 人点赞