【Vue3 Vue Router Pinia】中后台前端实战从路由守卫规范到落地实操彻底搞懂beforeEach最佳写法避开死循环、重复请求两大高频坑 文章目录一、开篇为什么路由守卫值得单独写一篇文章二、先搞清楚什么是路由守卫beforeEach 在哪个时机执行2.1 路由守卫的时机2.2 必须记住的 3 点三、beforeEach 里「应该做」什么3.1 适合做的事3.2 完整示例登录校验 标题设置四、beforeEach 里「不应该做」什么4.1 不适合做的事4.2 为什么「在 beforeEach 里发请求」容易出问题五、死循环为什么会发生怎么避免5.1 常见死循环场景5.2 正确写法白名单 明确分支六、重复请求如何避免6.1 问题示例6.2 正确做法在 Store 里做「请求去重」6.3 更推荐在应用启动或登录后拉取七、状态管理与路由守卫的配合规范7.1 职责划分7.2 推荐的项目流程八、完整实战示例九、小结beforeEach 使用清单 系列模块导航同学们好我是 Eugene尤金一名多年中后台前端开发工程师。Eugene 发音 /juːˈdʒiːn/大家怎么顺口怎么叫就好很多前端开发者都会遇到一个瓶颈代码能跑但不够规范功能能实现但维护起来特别痛苦一个人写没问题一到团队协作就各种混乱、踩坑、返工。想写出干净、优雅、可维护的专业代码靠的不是天赋而是体系化的规范 真实实战经验。这一系列《前端规范实战》我会用大白话 真实业务场景不讲玄学、不堆理论只分享能直接落地的规范、标准与避坑指南。帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。一、开篇为什么路由守卫值得单独写一篇文章在 Vue3 Vue Router 项目里beforeEach几乎是必用的。很多人写了不少业务代码但对「在 beforeEach 里该做什么、不该做什么」没有清晰规范经常出现死循环页面白屏或一直跳转重复请求接口被多次调用逻辑和登录状态、权限、面包屑等混在一起难以维护这篇文章的目标是结合日常实战讲清楚 beforeEach 的规范用法以及如何避免常见问题。不追求底层实现而是「日常该怎么写」。⬆ 返回目录二、先搞清楚什么是路由守卫beforeEach 在哪个时机执行2.1 路由守卫的时机路由守卫分为三种全局守卫、路由独享守卫、组件内守卫。本文重点讲 **全局前置守卫 **beforeEach。// router/index.jsimport{createRouter,createWebHistory}fromvue-routerconstroutercreateRouter({history:createWebHistory(),routes:[...]})// 全局前置守卫每次路由跳转「之前」都会执行router.beforeEach((to,from,next){// to: 要去的目标路由// from: 当前要离开的路由// next: 控制跳转的函数next()// 必须调用 next()否则路由不会继续})执行顺序可以简单理解为用户点击链接 / 执行router.push等路由开始切换beforeEach 被调用如果调用了next()→ 继续到目标路由如果调用next(false)→ 取消本次跳转如果调用next(/login)→ 重定向到其他路由⬆ 返回目录2.2 必须记住的 3 点要点说明必须调用 next()不调用 next路由会一直卡住只调用一次 next()多次调用会导致警告和异常行为在异步逻辑里也要保证调用 next例如发请求后要调用 next否则路由无法继续⬆ 返回目录三、beforeEach 里「应该做」什么3.1 适合做的事登录校验未登录用户访问需登录页面 → 重定向到登录页权限校验无权限访问某页面 → 重定向到 403 或首页设置页面标题document.title to.meta.title路由元信息加工面包屑、菜单高亮等轻量级状态同步如将「需要登录」标记写入 Vuex/Pinia核心原则快、简单、无副作用。⬆ 返回目录3.2 完整示例登录校验 标题设置// router/index.jsimport{createRouter,createWebHistory}fromvue-routerconstroutes[{path:/login,name:Login,component:()import(/views/Login.vue),meta:{title:登录,requiresAuth:false}},{path:/dashboard,name:Dashboard,component:()import(/views/Dashboard.vue),meta:{title:工作台,requiresAuth:true}}]constroutercreateRouter({history:createWebHistory(),routes})// 假设从 Pinia 获取登录状态import{useUserStore}from/stores/userrouter.beforeEach((to,from,next){// 1. 设置页面标题if(to.meta.title){document.title${to.meta.title}- 我的应用}// 2. 登录校验constuserStoreuseUserStore()constrequiresAuthto.matched.some(recordrecord.meta.requiresAuth)if(requiresAuth!userStore.isLoggedIn){next({path:/login,query:{redirect:to.fullPath}})return}// 3. 已登录访问登录页 → 直接去 redirect 或首页if(to.path/loginuserStore.isLoggedIn){next({path:(to.query.redirect||/dashboard).toString()})return}next()})说明requiresAuth通过to.matched.some(...)判断目标路由含父级是否要求登录redirect登录后可以跳回用户原来想去的页面每个分支内next()只调用一次逻辑清晰⬆ 返回目录四、beforeEach 里「不应该做」什么4.1 不适合做的事不要做原因发起接口请求如获取用户信息、菜单每次跳转都请求容易重复、阻塞不好控制复杂业务逻辑路由守卫应尽量轻量复杂逻辑放组件或 store修改全局状态如 Vuex 大量 mutation增加心智负担难以排查问题依赖尚未就绪的 store可能拿到空数据或导致时序问题⬆ 返回目录4.2 为什么「在 beforeEach 里发请求」容易出问题// ❌ 错误示例在 beforeEach 里发请求router.beforeEach(async(to,from,next){constuserStoreuseUserStore()if(!userStore.userInfo){awaitapi.getUserInfo()// 每次没 userInfo 就请求userStore.setUserInfo(...)}next()})典型问题重复请求多次快速切换路由userInfo还没写进 store会触发多次getUserInfo竞态多个请求返回顺序不确定后返回的可能覆盖先返回的阻塞路由请求较慢时用户会觉得「点了没反应」正确思路登录态、用户信息等在进入应用或登录成功后统一拉取守卫只做「读状态 判断」。⬆ 返回目录五、死循环为什么会发生怎么避免5.1 常见死循环场景场景 1未登录 → 去 /login又在 /login 里再次重定向// ❌ 死循环写法router.beforeEach((to,from,next){constuserStoreuseUserStore()if(!userStore.isLoggedIn){next(/login)// 未登录 → 去登录页}else{next()}})如果/login也被标记为requiresAuth: true或者你的判断逻辑把/login也当成「需要登录」的页面就会出现进入/login→ 判断未登录 →next(/login)→ 再次进入/login→ 无限循环场景 2重定向到自身// ❌ 死循环写法router.beforeEach((to,from,next){if(!hasPermission(to)){next(to.fullPath)// 没权限却重定向到当前页相当于循环}next()})⬆ 返回目录5.2 正确写法白名单 明确分支// ✅ 正确对登录页等做白名单处理constwhiteList[/login,/register,/forgot-password]router.beforeEach((to,from,next){constuserStoreuseUserStore()if(whiteList.includes(to.path)){// 白名单直接放行if(userStore.isLoggedInto.path/login){next(/dashboard)}else{next()}return}if(!userStore.isLoggedIn){next(/login?redirect${encodeURIComponent(to.fullPath)})return}next()})核心对/login等页面做白名单避免「进登录页 → 再判断未登录 → 再进登录页」的循环。⬆ 返回目录六、重复请求如何避免6.1 问题示例// ❌ 每次进页面都可能请求一次router.beforeEach(async(to,from,next){constuserStoreuseUserStore()if(to.meta.requiresAuth!userStore.userInfo){awaituserStore.fetchUserInfo()// 并发、重复、竞态}next()})多次快速切换路由时fetchUserInfo可能被调用多次。⬆ 返回目录6.2 正确做法在 Store 里做「请求去重」// stores/user.jsimport{defineStore}frompiniaexportconstuseUserStoredefineStore(user,{state:()({userInfo:null,userInfoPromise:null// 用 Promise 做去重}),actions:{asyncfetchUserInfo(){if(this.userInfo)returnthis.userInfoif(this.userInfoPromise)returnthis.userInfoPromisethis.userInfoPromiseapi.getUserInfo()try{this.userInfoawaitthis.userInfoPromisereturnthis.userInfo}finally{this.userInfoPromisenull}}}})守卫里只做「是否需要用户信息」的判断拉取逻辑全部在 store 中且通过userInfoPromise保证同一时刻只会有一个请求。⬆ 返回目录6.3 更推荐在应用启动或登录后拉取// main.jsimport{createApp}fromvueimportAppfrom./App.vueimportrouterfrom./routerimport{useUserStore}from./stores/userconstappcreateApp(App)app.use(router)router.isReady().then((){constuserStoreuseUserStore()if(userStore.token){userStore.fetchUserInfo()// 有 token 时预拉取}app.mount(#app)})这样用户信息在应用初始化时或登录后统一拉取beforeEach只读userStore.userInfo不做请求避免在路由切换时频繁发请求⬆ 返回目录七、状态管理与路由守卫的配合规范7.1 职责划分层级职责示例路由守卫读状态、做校验、决定是否放行判断isLoggedIn、hasPermissionStore存状态、发请求、提供方法fetchUserInfo、logout组件业务 UI、交互、调用 store调用userStore.fetchUserInfo等⬆ 返回目录7.2 推荐的项目流程用户打开页面 ↓ main.js有 token 时预拉取用户信息store ↓ 路由跳转 ↓ beforeEach读 store 判断是否登录、是否有权限 ↓ 放行 → 进入目标组件⬆ 返回目录八、完整实战示例下面是一个「登录校验 动态标题 登录后回跳」的示例。// router/index.jsimport{createRouter,createWebHistory}fromvue-routerimport{useUserStore}from/stores/userconstroutes[{path:/login,name:Login,component:()import(/views/Login.vue),meta:{title:登录,requiresAuth:false}},{path:/,component:()import(/layouts/MainLayout.vue),meta:{requiresAuth:true},children:[{path:dashboard,name:Dashboard,component:()import(/views/Dashboard.vue),meta:{title:工作台}},{path:profile,name:Profile,component:()import(/views/Profile.vue),meta:{title:个人中心}}]}]constroutercreateRouter({history:createWebHistory(),routes})constwhiteList[/login]router.beforeEach((to,from,next){// 1. 设置标题consttitleto.meta.title document.titletitle?${title}- 我的应用:我的应用// 2. 白名单直接放行但要处理已登录访问登录页if(whiteList.includes(to.path)){constuserStoreuseUserStore()if(to.path/loginuserStore.isLoggedIn){constredirect(to.query.redirect||/dashboard).toString()next(redirect)return}next()return}// 3. 需要登录的路由constuserStoreuseUserStore()constrequiresAuthto.matched.some(rr.meta.requiresAuth)if(requiresAuth!userStore.isLoggedIn){next({path:/login,query:{redirect:to.fullPath}})return}next()})exportdefaultrouter// stores/user.jsimport{defineStore}frompiniaexportconstuseUserStoredefineStore(user,{state:()({token:localStorage.getItem(token),userInfo:null}),getters:{isLoggedIn:(state)!!state.token},actions:{setUser(info,token){this.userInfoinfothis.tokentokenif(token)localStorage.setItem(token,token)},logout(){this.userInfonullthis.tokennulllocalStorage.removeItem(token)}}})// main.jsimport{createApp}fromvueimportAppfrom./App.vueimport{createPinia}frompiniaimportrouterfrom./routerimport{useUserStore}from./stores/userconstappcreateApp(App)app.use(createPinia())app.use(router)router.isReady().then((){constuserStoreuseUserStore()if(userStore.token){userStore.fetchUserInfo?.()// 如有封装在此预拉取}app.mount(#app)})⬆ 返回目录九、小结beforeEach 使用清单类别内容应该做登录/权限校验、标题设置、白名单、轻量逻辑不应该做在守卫里发请求、复杂业务、重状态修改避免死循环白名单处理登录页、不要重定向到当前页避免重复请求请求放到 store用 Promise 去重优先在启动/登录后拉取next 使用必须调用、只调用一次、每个分支都要调用按以上规范来写beforeEach可以避免绝大多数路由守卫相关的坑逻辑也会更清晰、好维护。⬆ 返回目录 系加粗样式列模块导航 状态管理与路由规范一、《Vue3 Pinia 状态管理规范状态拆分、Actions 写法、持久化实战避坑状态污染状态管理与路由规范篇》二、《Vue3 Pinia 状态管理规范何时用 Pinia 何时用本地状态状态管理与路由规范篇》三、《Vue Router 实战规范path/name/meta 配置 动态 / 嵌套路由统一团队标准状态管理与路由规范篇》四、《Vue3 Vue Router Pinia 路由守卫规范beforeEach 应做 / 不应做避死循环、防重复请求状态管理与路由规范篇》五、《Vue keep-alive 实战避坑include/exclude 路由 meta 标记中后台路由缓存精准可控状态管理与路由规范篇》 跟着系列慢慢学把技术功底扎扎实实地打牢 系列总览「前端规范实战系列」正在持续更新中后续会整理一篇《前端规范实战系列全系列目录导航》包含每篇文章简介 直达链接方便大家按顺序、体系化学习。更新中敬请期待⬆ 返回目录技术成长从来不是比谁写得快而是比谁写得稳、规范、可维护。哪怕每次只吃透一条规范长期下来差距会非常明显。后续我会持续更新前端规范、工程化、可维护代码相关实战干货帮你告别面条代码、维护噩梦在开发与面试中更有底气。觉得有用欢迎点赞 收藏 关注不错过每一篇实战内容。我是 Eugene与你一起写规范、写优质代码我们下篇干货见
Vue3 + Vue Router + Pinia 路由守卫规范:beforeEach 应做 / 不应做,避死循环、防重复请求|状态管理与路由规范篇
【Vue3 Vue Router Pinia】中后台前端实战从路由守卫规范到落地实操彻底搞懂beforeEach最佳写法避开死循环、重复请求两大高频坑 文章目录一、开篇为什么路由守卫值得单独写一篇文章二、先搞清楚什么是路由守卫beforeEach 在哪个时机执行2.1 路由守卫的时机2.2 必须记住的 3 点三、beforeEach 里「应该做」什么3.1 适合做的事3.2 完整示例登录校验 标题设置四、beforeEach 里「不应该做」什么4.1 不适合做的事4.2 为什么「在 beforeEach 里发请求」容易出问题五、死循环为什么会发生怎么避免5.1 常见死循环场景5.2 正确写法白名单 明确分支六、重复请求如何避免6.1 问题示例6.2 正确做法在 Store 里做「请求去重」6.3 更推荐在应用启动或登录后拉取七、状态管理与路由守卫的配合规范7.1 职责划分7.2 推荐的项目流程八、完整实战示例九、小结beforeEach 使用清单 系列模块导航同学们好我是 Eugene尤金一名多年中后台前端开发工程师。Eugene 发音 /juːˈdʒiːn/大家怎么顺口怎么叫就好很多前端开发者都会遇到一个瓶颈代码能跑但不够规范功能能实现但维护起来特别痛苦一个人写没问题一到团队协作就各种混乱、踩坑、返工。想写出干净、优雅、可维护的专业代码靠的不是天赋而是体系化的规范 真实实战经验。这一系列《前端规范实战》我会用大白话 真实业务场景不讲玄学、不堆理论只分享能直接落地的规范、标准与避坑指南。帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。一、开篇为什么路由守卫值得单独写一篇文章在 Vue3 Vue Router 项目里beforeEach几乎是必用的。很多人写了不少业务代码但对「在 beforeEach 里该做什么、不该做什么」没有清晰规范经常出现死循环页面白屏或一直跳转重复请求接口被多次调用逻辑和登录状态、权限、面包屑等混在一起难以维护这篇文章的目标是结合日常实战讲清楚 beforeEach 的规范用法以及如何避免常见问题。不追求底层实现而是「日常该怎么写」。⬆ 返回目录二、先搞清楚什么是路由守卫beforeEach 在哪个时机执行2.1 路由守卫的时机路由守卫分为三种全局守卫、路由独享守卫、组件内守卫。本文重点讲 **全局前置守卫 **beforeEach。// router/index.jsimport{createRouter,createWebHistory}fromvue-routerconstroutercreateRouter({history:createWebHistory(),routes:[...]})// 全局前置守卫每次路由跳转「之前」都会执行router.beforeEach((to,from,next){// to: 要去的目标路由// from: 当前要离开的路由// next: 控制跳转的函数next()// 必须调用 next()否则路由不会继续})执行顺序可以简单理解为用户点击链接 / 执行router.push等路由开始切换beforeEach 被调用如果调用了next()→ 继续到目标路由如果调用next(false)→ 取消本次跳转如果调用next(/login)→ 重定向到其他路由⬆ 返回目录2.2 必须记住的 3 点要点说明必须调用 next()不调用 next路由会一直卡住只调用一次 next()多次调用会导致警告和异常行为在异步逻辑里也要保证调用 next例如发请求后要调用 next否则路由无法继续⬆ 返回目录三、beforeEach 里「应该做」什么3.1 适合做的事登录校验未登录用户访问需登录页面 → 重定向到登录页权限校验无权限访问某页面 → 重定向到 403 或首页设置页面标题document.title to.meta.title路由元信息加工面包屑、菜单高亮等轻量级状态同步如将「需要登录」标记写入 Vuex/Pinia核心原则快、简单、无副作用。⬆ 返回目录3.2 完整示例登录校验 标题设置// router/index.jsimport{createRouter,createWebHistory}fromvue-routerconstroutes[{path:/login,name:Login,component:()import(/views/Login.vue),meta:{title:登录,requiresAuth:false}},{path:/dashboard,name:Dashboard,component:()import(/views/Dashboard.vue),meta:{title:工作台,requiresAuth:true}}]constroutercreateRouter({history:createWebHistory(),routes})// 假设从 Pinia 获取登录状态import{useUserStore}from/stores/userrouter.beforeEach((to,from,next){// 1. 设置页面标题if(to.meta.title){document.title${to.meta.title}- 我的应用}// 2. 登录校验constuserStoreuseUserStore()constrequiresAuthto.matched.some(recordrecord.meta.requiresAuth)if(requiresAuth!userStore.isLoggedIn){next({path:/login,query:{redirect:to.fullPath}})return}// 3. 已登录访问登录页 → 直接去 redirect 或首页if(to.path/loginuserStore.isLoggedIn){next({path:(to.query.redirect||/dashboard).toString()})return}next()})说明requiresAuth通过to.matched.some(...)判断目标路由含父级是否要求登录redirect登录后可以跳回用户原来想去的页面每个分支内next()只调用一次逻辑清晰⬆ 返回目录四、beforeEach 里「不应该做」什么4.1 不适合做的事不要做原因发起接口请求如获取用户信息、菜单每次跳转都请求容易重复、阻塞不好控制复杂业务逻辑路由守卫应尽量轻量复杂逻辑放组件或 store修改全局状态如 Vuex 大量 mutation增加心智负担难以排查问题依赖尚未就绪的 store可能拿到空数据或导致时序问题⬆ 返回目录4.2 为什么「在 beforeEach 里发请求」容易出问题// ❌ 错误示例在 beforeEach 里发请求router.beforeEach(async(to,from,next){constuserStoreuseUserStore()if(!userStore.userInfo){awaitapi.getUserInfo()// 每次没 userInfo 就请求userStore.setUserInfo(...)}next()})典型问题重复请求多次快速切换路由userInfo还没写进 store会触发多次getUserInfo竞态多个请求返回顺序不确定后返回的可能覆盖先返回的阻塞路由请求较慢时用户会觉得「点了没反应」正确思路登录态、用户信息等在进入应用或登录成功后统一拉取守卫只做「读状态 判断」。⬆ 返回目录五、死循环为什么会发生怎么避免5.1 常见死循环场景场景 1未登录 → 去 /login又在 /login 里再次重定向// ❌ 死循环写法router.beforeEach((to,from,next){constuserStoreuseUserStore()if(!userStore.isLoggedIn){next(/login)// 未登录 → 去登录页}else{next()}})如果/login也被标记为requiresAuth: true或者你的判断逻辑把/login也当成「需要登录」的页面就会出现进入/login→ 判断未登录 →next(/login)→ 再次进入/login→ 无限循环场景 2重定向到自身// ❌ 死循环写法router.beforeEach((to,from,next){if(!hasPermission(to)){next(to.fullPath)// 没权限却重定向到当前页相当于循环}next()})⬆ 返回目录5.2 正确写法白名单 明确分支// ✅ 正确对登录页等做白名单处理constwhiteList[/login,/register,/forgot-password]router.beforeEach((to,from,next){constuserStoreuseUserStore()if(whiteList.includes(to.path)){// 白名单直接放行if(userStore.isLoggedInto.path/login){next(/dashboard)}else{next()}return}if(!userStore.isLoggedIn){next(/login?redirect${encodeURIComponent(to.fullPath)})return}next()})核心对/login等页面做白名单避免「进登录页 → 再判断未登录 → 再进登录页」的循环。⬆ 返回目录六、重复请求如何避免6.1 问题示例// ❌ 每次进页面都可能请求一次router.beforeEach(async(to,from,next){constuserStoreuseUserStore()if(to.meta.requiresAuth!userStore.userInfo){awaituserStore.fetchUserInfo()// 并发、重复、竞态}next()})多次快速切换路由时fetchUserInfo可能被调用多次。⬆ 返回目录6.2 正确做法在 Store 里做「请求去重」// stores/user.jsimport{defineStore}frompiniaexportconstuseUserStoredefineStore(user,{state:()({userInfo:null,userInfoPromise:null// 用 Promise 做去重}),actions:{asyncfetchUserInfo(){if(this.userInfo)returnthis.userInfoif(this.userInfoPromise)returnthis.userInfoPromisethis.userInfoPromiseapi.getUserInfo()try{this.userInfoawaitthis.userInfoPromisereturnthis.userInfo}finally{this.userInfoPromisenull}}}})守卫里只做「是否需要用户信息」的判断拉取逻辑全部在 store 中且通过userInfoPromise保证同一时刻只会有一个请求。⬆ 返回目录6.3 更推荐在应用启动或登录后拉取// main.jsimport{createApp}fromvueimportAppfrom./App.vueimportrouterfrom./routerimport{useUserStore}from./stores/userconstappcreateApp(App)app.use(router)router.isReady().then((){constuserStoreuseUserStore()if(userStore.token){userStore.fetchUserInfo()// 有 token 时预拉取}app.mount(#app)})这样用户信息在应用初始化时或登录后统一拉取beforeEach只读userStore.userInfo不做请求避免在路由切换时频繁发请求⬆ 返回目录七、状态管理与路由守卫的配合规范7.1 职责划分层级职责示例路由守卫读状态、做校验、决定是否放行判断isLoggedIn、hasPermissionStore存状态、发请求、提供方法fetchUserInfo、logout组件业务 UI、交互、调用 store调用userStore.fetchUserInfo等⬆ 返回目录7.2 推荐的项目流程用户打开页面 ↓ main.js有 token 时预拉取用户信息store ↓ 路由跳转 ↓ beforeEach读 store 判断是否登录、是否有权限 ↓ 放行 → 进入目标组件⬆ 返回目录八、完整实战示例下面是一个「登录校验 动态标题 登录后回跳」的示例。// router/index.jsimport{createRouter,createWebHistory}fromvue-routerimport{useUserStore}from/stores/userconstroutes[{path:/login,name:Login,component:()import(/views/Login.vue),meta:{title:登录,requiresAuth:false}},{path:/,component:()import(/layouts/MainLayout.vue),meta:{requiresAuth:true},children:[{path:dashboard,name:Dashboard,component:()import(/views/Dashboard.vue),meta:{title:工作台}},{path:profile,name:Profile,component:()import(/views/Profile.vue),meta:{title:个人中心}}]}]constroutercreateRouter({history:createWebHistory(),routes})constwhiteList[/login]router.beforeEach((to,from,next){// 1. 设置标题consttitleto.meta.title document.titletitle?${title}- 我的应用:我的应用// 2. 白名单直接放行但要处理已登录访问登录页if(whiteList.includes(to.path)){constuserStoreuseUserStore()if(to.path/loginuserStore.isLoggedIn){constredirect(to.query.redirect||/dashboard).toString()next(redirect)return}next()return}// 3. 需要登录的路由constuserStoreuseUserStore()constrequiresAuthto.matched.some(rr.meta.requiresAuth)if(requiresAuth!userStore.isLoggedIn){next({path:/login,query:{redirect:to.fullPath}})return}next()})exportdefaultrouter// stores/user.jsimport{defineStore}frompiniaexportconstuseUserStoredefineStore(user,{state:()({token:localStorage.getItem(token),userInfo:null}),getters:{isLoggedIn:(state)!!state.token},actions:{setUser(info,token){this.userInfoinfothis.tokentokenif(token)localStorage.setItem(token,token)},logout(){this.userInfonullthis.tokennulllocalStorage.removeItem(token)}}})// main.jsimport{createApp}fromvueimportAppfrom./App.vueimport{createPinia}frompiniaimportrouterfrom./routerimport{useUserStore}from./stores/userconstappcreateApp(App)app.use(createPinia())app.use(router)router.isReady().then((){constuserStoreuseUserStore()if(userStore.token){userStore.fetchUserInfo?.()// 如有封装在此预拉取}app.mount(#app)})⬆ 返回目录九、小结beforeEach 使用清单类别内容应该做登录/权限校验、标题设置、白名单、轻量逻辑不应该做在守卫里发请求、复杂业务、重状态修改避免死循环白名单处理登录页、不要重定向到当前页避免重复请求请求放到 store用 Promise 去重优先在启动/登录后拉取next 使用必须调用、只调用一次、每个分支都要调用按以上规范来写beforeEach可以避免绝大多数路由守卫相关的坑逻辑也会更清晰、好维护。⬆ 返回目录 系加粗样式列模块导航 状态管理与路由规范一、《Vue3 Pinia 状态管理规范状态拆分、Actions 写法、持久化实战避坑状态污染状态管理与路由规范篇》二、《Vue3 Pinia 状态管理规范何时用 Pinia 何时用本地状态状态管理与路由规范篇》三、《Vue Router 实战规范path/name/meta 配置 动态 / 嵌套路由统一团队标准状态管理与路由规范篇》四、《Vue3 Vue Router Pinia 路由守卫规范beforeEach 应做 / 不应做避死循环、防重复请求状态管理与路由规范篇》五、《Vue keep-alive 实战避坑include/exclude 路由 meta 标记中后台路由缓存精准可控状态管理与路由规范篇》 跟着系列慢慢学把技术功底扎扎实实地打牢 系列总览「前端规范实战系列」正在持续更新中后续会整理一篇《前端规范实战系列全系列目录导航》包含每篇文章简介 直达链接方便大家按顺序、体系化学习。更新中敬请期待⬆ 返回目录技术成长从来不是比谁写得快而是比谁写得稳、规范、可维护。哪怕每次只吃透一条规范长期下来差距会非常明显。后续我会持续更新前端规范、工程化、可维护代码相关实战干货帮你告别面条代码、维护噩梦在开发与面试中更有底气。觉得有用欢迎点赞 收藏 关注不错过每一篇实战内容。我是 Eugene与你一起写规范、写优质代码我们下篇干货见