uni-router v2.1.0 升级:导航守卫全面支持返回值模式
v2.1.0 将导航守卫全面升级为返回值模式与 Vue Router 4.x 一致通过return undefined/return false/return RouteLocationRaw控制导航行为无需调用next()回调。旧版next()回调模式保持兼容标记为已弃用。前言meng-xi/uni-router的守卫系统自 v1.0 起一直使用next()回调模式控制导航行为。Vue Router 4.x 已全面移除next()回调改为返回值模式代码更简洁、更符合 async/await 风格。v2.1.0 将守卫升级为返回值模式同时保持对旧版next()回调的完全兼容提供平滑迁移路径。一、问题分析1.next()回调容易忘记调用// v2.0.x — 忘记调用 next() 导致导航挂起router.beforeEach((to,from,next){constvalidawaitcheckToken()if(!valid){// 忘记调用 next()导航永久挂起}next()})2. 回调嵌套使代码冗长// v2.0.x — 回调嵌套可读性差router.beforeEach((to,from,next){if(to.meta.requireAuth){checkAuth(result{if(result){next()}else{next({name:login})}})}else{next()}})3.afterEach无法区分导航成功/失败// v2.0.x — afterEach 不知道导航是否成功router.afterEach((to,from){// 无法判断导航是否被守卫中止// 无法判断 uni API 调用是否失败})二、新增能力1. 守卫返回值模式v2.1.0 引入 Vue Router 4.x 风格的返回值模式守卫通过返回值控制导航行为// v2.1.0 — 返回值模式router.beforeEach((to,from){if(to.meta.requireAuth!isLoggedIn()){return{name:login}// 重定向}// 不返回值或 return true 表示放行})// 异步守卫router.beforeEach(async(to,from){constvalidawaitcheckToken()if(!valid)returnfalse// 中止})返回值对照表返回值行为undefined/void/true放行继续执行下一个守卫false中止导航NAVIGATION_ABORTEDstring如/login重定向到路径RouteLocationRaw如{ name: login }重定向到路由位置Error对象取消导航NAVIGATION_CANCELLED抛出异常取消导航NAVIGATION_CANCELLED2. 可控重定向的返回值写法// v2.1.0 — 通过返回值中的 mode 字段指定重定向方式router.beforeEach((to,from){if(to.meta.requireAuth!isLoggedIn()){return{location:{name:login},mode:replace}}if(to.meta.roles!hasRole(to.meta.roles)){return{location:{name:home},mode:relaunch}}})3.afterEach接收failure参数// v2.1.0 — afterEach 可区分导航成功/失败router.afterEach((to,from,failure){if(failure){console.error(导航失败:,failure.message)return}// 导航成功设置页面标题if(to.meta.title){uni.setNavigationBarTitle({title:to.meta.titleasstring})}})4.NavigationGuardReturn类型typeNavigationGuardReturnvoid|undefined|boolean|RouteLocationRaw|Error|null三、Bug 修复1.next()未调用导致导航挂起修复前next()回调模式中忘记调用next()会导致导航永久挂起需要超时机制兜底但超时后中止导航而非放行。修复后返回值模式中不返回值等同于return undefined自动放行。旧版回调模式保持超时保护。2. 守卫中止后afterEach缺少失败信息修复前守卫中止导航时afterEach无法获取NavigationFailure信息。修复后守卫中止、uni API 调用失败等场景afterEach的第三个参数failure会传入对应的NavigationFailure实例。四、架构设计守卫模式自动检测通过函数参数个数guard.length自动识别守卫模式guard.length 3 → (to, from, next) → 回调模式兼容旧版 guard.length 3 → (to, from) → 返回值模式推荐functionrunGuard(guard,to,from,timeout){constuseNextCallbackguard.length3if(useNextCallback){returnrunGuardWithNext(guard,to,from,timeout)}returnrunGuardWithReturn(guard,to,from,timeout)}返回值模式执行流程守卫执行 ├── 返回值 undefined / true / null → 放行 ├── 返回值 false → 中止NAVIGATION_ABORTED ├── 返回值 RouteLocationRaw → 重定向 ├── 返回值 Error → 取消NAVIGATION_CANCELLED ├── 抛出异常 → 取消NAVIGATION_CANCELLED └── 超时 → 取消NAVIGATION_CANCELLED混用检测同时使用next()回调和返回值的守卫会在控制台输出警告Navigation guard guardName called next() and also returned a value. Use either next() callback or return value, not both.五、完整示例基础导航守卫import{createRouter}frommeng-xi/uni-routerconstroutercreateRouter({routes:[{path:pages/index/index,name:home},{path:pages/login/login,name:login},{path:pages/protected/protected,name:protected,meta:{requireAuth:true}}]})// 返回值模式推荐router.beforeEach((to,from){if(to.meta.requireAuth!isLoggedIn()){return{name:login}}})// 异步守卫router.beforeEach(async(to,from){constuserawaitfetchUser()if(to.meta.roles!user.roles.includes(to.meta.roles)){return{name:403}}})// 后置钩子接收 failure 参数router.afterEach((to,from,failure){if(failure){console.error(导航失败:,failure.message)return}console.log(导航成功:${from.path}→${to.path})})可控重定向router.beforeEach((to,from){if(to.nameprotected!isLoggedIn()){// replace 模式登录后不保留受保护页面的历史return{location:{name:login},mode:replace}}if(to.meta.roles!hasRole(to.meta.roles)){// relaunch 模式清空栈回到首页return{location:{name:home},mode:relaunch}}})离开确认router.beforeEach((to,from){if(from.meta.dirty){returnnewPromise(resolve{uni.showModal({title:提示,content:有未保存的修改确认离开,success:res{resolve(res.confirm?true:false)}})})}})六、升级指南v2.1.0 完全向后兼容无需修改现有代码即可升级。推荐迁移推荐逐步将守卫从next()回调模式迁移到返回值模式// 迁移前router.beforeEach((to,from,next){if(condition){next({name:login})}else{next()}})// 迁移后router.beforeEach((to,from){if(condition){return{name:login}}})新旧对照表场景旧版next()回调新版返回值放行next()return undefined或不写放行显式next()return true中止next(false)return false重定向next({ name: login })return { name: login }重定向方式next({ name: login }, { mode: replace })return { location: { name: login }, mode: replace }抛出错误next(new Error(msg))throw new Error(msg)返回错误—return new Error(msg)不需要改动使用next()回调的旧守卫代码无需修改保持完全兼容守卫注册 APIrouter.beforeEach/beforeResolve/afterEach/beforeEnter签名不变守卫移除函数返回值不受影响超时配置guardTimeout不受影响版本兼容性功能v2.0.xv2.1.0next()回调模式支持支持已弃用返回值模式不支持支持afterEach接收failure不支持支持混用检测警告无有