v1.7.0 为守卫的 next() 回调新增可选 options 参数,支持在重定向时显式指定导航方式(push / replace / relaunch),不再被原始导航方式绑定
前言
在 v1.7.0 之前,守卫中调用 next(location) 重定向时,跳转方式完全由"触发守卫的那次导航方法"决定:router.push 触发的守卫重定向时仍用 uni.navigateTo,router.replace 触发的则用 uni.redirectTo。
这意味着一个常见场景无法实现:无论用户是从 push 还是 replace 进入受保护页面,未登录时都希望用 replace 跳转登录页,避免登录页之后残留多余的历史页面。v1.7.0 解决了这一限制。
一、问题分析
旧版行为
守卫的 next() 回调签名是:
type NavigationGuardNext = (to?: RouteLocationRaw | false) => void
handleGuardResult 中重定向时直接透传原始 mode:
// router/index.ts(v1.6.3)
if (result.redirect) {
const redirectTarget = this.matcher.resolve(result.redirect)
return this.executeNavigation(redirectTarget, from, mode, redirectDepth + 1, …)
// ^^^^ 原始 mode 被沿用
}
| router.push | uni.navigateTo |
| router.replace | uni.redirectTo |
| router.relaunch | uni.reLaunch |
| router.back | 落入 else 分支 → uni.reLaunch |
使用者在守卫内无法单独指定重定向方式。
二、新增能力
1. 扩展 next() 签名
type NavigationGuardNext = (
to?: RouteLocationRaw | false,
options?: NavigationGuardNextOptions
) => void
interface NavigationGuardNextOptions {
mode?: NavigationRedirectMode
}
type NavigationRedirectMode = 'push' | 'replace' | 'relaunch'
options.mode 仅在 next(location) 重定向时生效,对 next() 和 next(false) 无影响。
2. 重定向方式优先级
// router/index.ts(v1.7.0)
const redirectMode = result.mode ?? (mode === 'back' ? 'relaunch' : mode)
- 守卫指定优先:next(location, { mode: 'replace' }) 显式指定时使用 replace
- 未指定则沿用原始:next(location) 时沿用触发守卫的原始导航方式(向后兼容)
- back 回退 relaunch:原始导航为 back 时,未指定 mode 则回退为 relaunch(因 back 无法跳转到页面栈外目标,保持 v1.6.3 的实际语义)
三、使用示例
场景一:登录拦截统一用 replace
最典型的应用场景。无论用户从哪种导航方式进入受保护页面,未登录时都强制用 replace 跳转登录页,避免登录后返回时出现受保护页面的残留历史:
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth && !isLoggedIn()) {
// ✅ 强制 replace,登录页之后不会残留受保护页面的历史
next({ name: 'login', query: { redirect: to.fullPath } }, { mode: 'replace' })
} else {
next()
}
})
对比旧版:
// ❌ 旧版无法控制,重定向方式由触发守卫的导航决定
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth && !isLoggedIn()) {
next({ name: 'login', query: { redirect: to.fullPath } })
// 若用户通过 router.push 进入,重定向会用 navigateTo,登录页之后会残留受保护页面
} else {
next()
}
})
场景二:退出登录后重置页面栈
退出登录后通常希望清空整个页面栈再跳转登录页,避免用户通过返回键回到已退出登录的页面:
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth && !isLoggedIn()) {
// ✅ 强制 relaunch,关闭所有页面后打开登录页
next({ name: 'login' }, { mode: 'relaunch' })
} else {
next()
}
})
场景三:保持原有行为(向后兼容)
不传 options 时行为与 v1.6.3 完全一致,现有代码无需任何修改:
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth && !isLoggedIn()) {
next({ name: 'login', query: { redirect: to.fullPath } })
// 沿用原始导航方式,与 v1.6.3 行为一致
} else {
next()
}
})
四、mode 取值对照
| 'push' | router.push() | uni.navigateTo | 保留当前页面,跳转到新页面 |
| 'replace' | router.replace() | uni.redirectTo | 关闭当前页面,跳转到新页面 |
| 'relaunch' | router.relaunch() | uni.reLaunch | 关闭所有页面,打开新页面 |
::: tip 注意事项
- 重定向方式仅影响跳转 API 的选择,守卫链、动画、事件等行为保持一致
- mode 仅在 next(location) 重定向时生效,对 next() 和 next(false) 无影响
- 重定向仍会触发新的导航流程(重新执行所有守卫),最大重定向深度仍为 10 次
:::
升级指南
v1.7.0 是完全向后兼容的新功能版本,无需修改任何现有代码即可升级。
行为变化
| next(location) | 沿用原始导航方式 | 沿用原始导航方式(无变化) |
| next(location, { mode }) | 不支持 | 使用指定的 mode 重定向 |
| back 触发的重定向 | 实际走 relaunch | 未指定 mode 时仍回退 relaunch(无变化) |
| 其他导航方式触发的重定向 | 沿用原始方式 | 未指定 mode 时沿用原始方式(无变化) |
新增类型导出
- NavigationGuardNextOptions — next() 回调的可选参数类型
- NavigationRedirectMode — 重定向方式类型('push' | 'replace' | 'relaunch')



