航守衛(wèi)全面支持返回值模式)
v2.1.0 將導(dǎo)航守衛(wèi)全面升級為返回值模式與 Vue Router 4.x 一致通過return undefined/return false/return RouteLocationRaw控制導(dǎo)航行為無需調(diào)用next()回調(diào)。舊版next()回調(diào)模式保持兼容標記為已棄用。前言meng-xi/uni-router的守衛(wèi)系統(tǒng)自 v1.0 起一直使用next()回調(diào)模式控制導(dǎo)航行為。Vue Router 4.x 已全面移除next()回調(diào)改為返回值模式代碼更簡潔、更符合 async/await 風(fēng)格。v2.1.0 將守衛(wèi)升級為返回值模式同時保持對舊版next()回調(diào)的完全兼容提供平滑遷移路徑。一、問題分析1.next()回調(diào)容易忘記調(diào)用// v2.0.x — 忘記調(diào)用 next() 導(dǎo)致導(dǎo)航掛起router.beforeEach((to,from,next){constvalidawaitcheckToken()if(!valid){// 忘記調(diào)用 next()導(dǎo)航永久掛起}next()})2. 回調(diào)嵌套使代碼冗長// v2.0.x — 回調(diào)嵌套可讀性差router.beforeEach((to,from,next){if(to.meta.requireAuth){checkAuth(result{if(result){next()}else{next({name:login})}})}else{next()}})3.afterEach無法區(qū)分導(dǎo)航成功/失敗// v2.0.x — afterEach 不知道導(dǎo)航是否成功router.afterEach((to,from){// 無法判斷導(dǎo)航是否被守衛(wèi)中止// 無法判斷 uni API 調(diào)用是否失敗})二、新增能力1. 守衛(wèi)返回值模式v2.1.0 引入 Vue Router 4.x 風(fēng)格的返回值模式守衛(wèi)通過返回值控制導(dǎo)航行為// v2.1.0 — 返回值模式router.beforeEach((to,from){if(to.meta.requireAuth!isLoggedIn()){return{name:login}// 重定向}// 不返回值或 return true 表示放行})// 異步守衛(wèi)router.beforeEach(async(to,from){constvalidawaitcheckToken()if(!valid)returnfalse// 中止})返回值對照表返回值行為undefined/void/true放行繼續(xù)執(zhí)行下一個守衛(wèi)false中止導(dǎo)航NAVIGATION_ABORTEDstring如/login重定向到路徑RouteLocationRaw如{ name: login }重定向到路由位置Error對象取消導(dǎo)航NAVIGATION_CANCELLED拋出異常取消導(dǎo)航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參數(shù)// v2.1.0 — afterEach 可區(qū)分導(dǎo)航成功/失敗router.afterEach((to,from,failure){if(failure){console.error(導(dǎo)航失敗:,failure.message)return}// 導(dǎo)航成功設(shè)置頁面標題if(to.meta.title){uni.setNavigationBarTitle({title:to.meta.titleasstring})}})4.NavigationGuardReturn類型typeNavigationGuardReturnvoid|undefined|boolean|RouteLocationRaw|Error|null三、Bug 修復(fù)1.next()未調(diào)用導(dǎo)致導(dǎo)航掛起修復(fù)前next()回調(diào)模式中忘記調(diào)用next()會導(dǎo)致導(dǎo)航永久掛起需要超時機制兜底但超時后中止導(dǎo)航而非放行。修復(fù)后返回值模式中不返回值等同于return undefined自動放行。舊版回調(diào)模式保持超時保護。2. 守衛(wèi)中止后afterEach缺少失敗信息修復(fù)前守衛(wèi)中止導(dǎo)航時afterEach無法獲取NavigationFailure信息。修復(fù)后守衛(wèi)中止、uni API 調(diào)用失敗等場景afterEach的第三個參數(shù)failure會傳入對應(yīng)的NavigationFailure實例。四、架構(gòu)設(shè)計守衛(wèi)模式自動檢測通過函數(shù)參數(shù)個數(shù)guard.length自動識別守衛(wèi)模式guard.length 3 → (to, from, next) → 回調(diào)模式兼容舊版 guard.length 3 → (to, from) → 返回值模式推薦functionrunGuard(guard,to,from,timeout){constuseNextCallbackguard.length3if(useNextCallback){returnrunGuardWithNext(guard,to,from,timeout)}returnrunGuardWithReturn(guard,to,from,timeout)}返回值模式執(zhí)行流程守衛(wèi)執(zhí)行 ├── 返回值 undefined / true / null → 放行 ├── 返回值 false → 中止NAVIGATION_ABORTED ├── 返回值 RouteLocationRaw → 重定向 ├── 返回值 Error → 取消NAVIGATION_CANCELLED ├── 拋出異常 → 取消NAVIGATION_CANCELLED └── 超時 → 取消NAVIGATION_CANCELLED混用檢測同時使用next()回調(diào)和返回值的守衛(wèi)會在控制臺輸出警告Navigation guard guardName called next() and also returned a value. Use either next() callback or return value, not both.五、完整示例基礎(chǔ)導(dǎo)航守衛(wèi)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}}})// 異步守衛(wèi)router.beforeEach(async(to,from){constuserawaitfetchUser()if(to.meta.roles!user.roles.includes(to.meta.roles)){return{name:403}}})// 后置鉤子接收 failure 參數(shù)router.afterEach((to,from,failure){if(failure){console.error(導(dǎo)航失敗:,failure.message)return}console.log(導(dǎo)航成功:${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 模式清空?;氐绞醉搑eturn{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 完全向后兼容無需修改現(xiàn)有代碼即可升級。推薦遷移推薦逐步將守衛(wèi)從next()回調(diào)模式遷移到返回值模式// 遷移前router.beforeEach((to,from,next){if(condition){next({name:login})}else{next()}})// 遷移后router.beforeEach((to,from){if(condition){return{name:login}}})新舊對照表場景舊版next()回調(diào)新版返回值放行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()回調(diào)的舊守衛(wèi)代碼無需修改保持完全兼容守衛(wèi)注冊 APIrouter.beforeEach/beforeResolve/afterEach/beforeEnter簽名不變守衛(wèi)移除函數(shù)返回值不受影響超時配置guardTimeout不受影響版本兼容性功能v2.0.xv2.1.0next()回調(diào)模式支持支持已棄用返回值模式不支持支持afterEach接收failure不支持支持混用檢測警告無有