uni.addInterceptor是唯一可靠入口,因其在uni.navigateTo等跳转API发起前同步执行invoke钩子,返回false可中断跳转;而onLaunch/onShow和页面生命周期无法覆盖分享卡片、后台唤醒等场景,且switchTab等原生跳转无Vue Router式导航守卫机制。

小程序端无法靠监听页面生命周期或监听 popstate 实现真正的“拦截→阻断→重定向”,必须用 uni.addInterceptor + 路径标准化 + 异步校验兜底,否则登录跳转、权限拦截必然漏掉。
为什么 uni.addInterceptor 是唯一可靠入口
小程序的跳转行为(uni.navigateTo、uni.redirectTo、uni.reLaunch、uni.switchTab)全部走原生 API,没有 Vue Router 那种同步可中断的导航流程。invoke 钩子是唯一能在跳转发起前执行逻辑的地方;success/fail 已跳完,拦不住。
-
invoke返回false或不返回,会直接中断跳转 - 返回修改后的
args(比如改args.url),uni-app 会继续执行新路径跳转 - 只拦截
navigateTo不够——redirectTo和reLaunch常用于登录后回首页,漏掉就等于守卫失效 -
switchTab的args.url不带pages/前缀(如'/tabBar/mine'),白名单比对前必须 normalize
如何安全提取路径并匹配白名单
URL 可能带 query(如 /pages/user/profile?id=123),直接比对会失败;小程序还可能出现相对路径(如 '../login/login'),必须统一处理成标准绝对路径再判断。
- 用
args.url.split('?')[0]剥离参数,再做白名单检查 - 对
switchTab,建议白名单统一写成不含pages/的格式(如'/tabBar/mine'),避免硬编码差异 - 不要依赖
getCurrentPages()推导当前路径——它在跳转中途可能不准,且 H5/App 端行为不一致 - 路径比较前用
url.replace(/^\.+\//, '')清理相对路径前缀,再补'/'开头确保格式统一
token 校验不能只看存不存在
uni.getStorageSync('token') 可能返回 null、undefined、空字符串、甚至 JSON 字符串,直接 !!token 会误判。
- 必须显式判断:
typeof token === 'string' && token.trim().length > 0 - 如果后端存的是对象(如
{ token: 'xxx', exp: 171xxxxx }),要先JSON.parse(token)再取字段,否则token.exp报错 - 过期检查别漏乘 1000:
Date.now() > parsedToken.exp * 1000 - 拦截器里别调
uni.navigateTo—— 会导致递归调用,栈溢出
重定向时怎么避免无限跳转
强制跳转登录页(如 args.url = '/pages/auth/login')后,若用户再从登录页跳其他受保护页面,又会触发拦截,形成循环。必须加防重复逻辑。
- 在重定向前检查目标路径是否已是登录页:
args.url.includes('/pages/auth/login') - 更稳妥的做法:用全局 flag(如
uni.setStorageSync('isRedirecting', true))标记跳转中状态,invoke开头读 flag 并 return,跳转完成后再清 flag - 登录页自身也要在
onLoad里清掉 auth 相关缓存,避免冷启动时 token 过期但页面仍显示“已登录”
真正难的不是写几行拦截代码,而是把所有跳转入口收口、路径标准化、token 校验边界全覆盖、重定向防循环这四件事全做严实——漏一个,守卫就形同虚设。


















