7.2 路由进阶:守卫链与按需加载


文档摘要

7.2 路由进阶:守卫链与按需加载 本节摘要:每次导航要过一条由全局守卫、路由独享守卫、组件内守卫组成的拦截链,然后才解析异步组件并挂载。本节拆解守卫的精确执行顺序,实现一套登录加权限的完整方案(含循环重定向的排查),并用路由懒加载完成页面级分包。 你应当能获得的能力 阅读完本节,你应当能够: 按序写出一次导航触发的全部守卫与钩子; 实现全局登录鉴权与基于角色的路由权限,处理好重定向闭环; 配置路由懒加载并验证分包效果; 用路由元信息承载权限声明,让守卫保持通用。

7.2 路由进阶:守卫链与按需加载

本节摘要:每次导航要过一条由全局守卫、路由独享守卫、组件内守卫组成的拦截链,然后才解析异步组件并挂载。本节拆解守卫的精确执行顺序,实现一套登录加权限的完整方案(含循环重定向的排查),并用路由懒加载完成页面级分包。

你应当能获得的能力

阅读完本节,你应当能够:

  1. 按序写出一次导航触发的全部守卫与钩子;
  2. 实现全局登录鉴权与基于角色的路由权限,处理好重定向闭环;
  3. 配置路由懒加载并验证分包效果;
  4. 用路由元信息承载权限声明,让守卫保持通用。

一、完整导航流程

一次从 /a 到 /b 的导航,时序如下(Vue Router 4):

导航守卫执行时序

导航守卫执行时序

守卫三件套的分工记忆法:全局管横切面(登录、埋点),独享管单路由(入口限制),组件内管自身(未保存提醒、参数响应)。守卫里的 next 调用规则:Vue Router 4 也支持返回值风格(return false / return '/login'),一支团队统一一种风格即可,混用是维护灾难。

二、登录鉴权:标准实现

权限信息放路由元信息 meta 上,守卫保持通用:

const routes = [ { path: '/login', component: Login }, { path: '/finance', component: Finance, meta: { requiresAuth: true, roles: ['admin', 'finance'] } } ]; router.beforeEach(async (to) => { const userStore = useUserStore(); if (to.meta.requiresAuth && !userStore.isLoggedIn) { return { path: '/login', query: { redirect: to.fullPath } }; // 记下来路 } if (to.meta.roles && !to.meta.roles.includes(userStore.role)) { return { path: '/403' }; } return true; // 放行 });

登录成功后跳回记录的来路:

router.push(route.query.redirect || '/');

循环重定向排查:症状是页面在 login 与目标页之间反复横跳,控制台报重定向链。成因几乎总有三类:守卫里 return 的地址本身也命中同一条守卫规则(login 页配了 requiresAuth);登录态判断依据与真实状态不同步(token 存了、store 没刷新);异步恢复登录态时守卫提前放行。第三类的规范解法——在守卫里等恢复完成:

let restored = false; router.beforeEach(async (to) => { if (!restored) { await useUserStore().restoreFromStorage(); // 应用首次导航前恢复会话 restored = true; } // ...后续判断 });

三、组件内守卫的两个正位用法

未保存提醒是 beforeRouteLeave 的教科书场景:

onBeforeRouteLeave(() => { if (hasUnsavedChanges.value && !window.confirm('有未保存的修改,确定离开?')) { return false; } return true; });

参数变化响应归 beforeRouteUpdate(或 watch 参数,二选一):/user/1/user/2 组件复用,需要在此重新拉数据。

一个常见反模式要点名:在全局 beforeEach 里调接口校验每个导航的权限。每次换页一次请求,页面切换被网络延迟绑架。正确做法是登录时一次性拉取权限清单存进仓库(或状态对象),守卫只做本地判断,权限变更时显式刷新。

四、懒加载:路由级的按需分包

// 每个页面组件改成动态 import,构建工具自动按页面分包 const routes = [ { path: '/', component: () => import('../views/Home.vue') }, { path: '/chart', component: () => import('../views/Chart.vue') }, // 重页面单独成包 { path: '/admin', component: () => import('../views/AdminLayout.vue'), children: [ { path: 'users', component: () => import('../views/admin/Users.vue') } ] } ];

从管线视角看懒加载的收益:首屏只下载与渲染首页所需的代码,图表页那一大包代码在用户点击导航时才由守卫流程中的"解析异步组件"阶段拉取。配合 Vite 的分包策略,可以进一步把公共依赖拆出来避免重复下载。代价是首次进入子页面有网络等待——用路由组件的加载态(defineAsyncComponent 的 loading 参数或全局进度条)盖住白屏。

粒度纪律:路由级懒加载默认全开没问题,但同屏常切换的兄弟路由别为了懒而懒——两三个小页面合成一个包,比每次切换都发请求体验更好。

本节要点回顾

  • 时序:前置 → 独享 → 离开 → 异步组件解析 → 进入 → 解析守卫 → 确认 → 挂载 → 后置;
  • 分工:全局横切、独享单路由、组件内自身,next/return 风格全队统一;
  • 鉴权正位:meta 声明要求、守卫通用判断、登录页记得豁免、异步恢复会话要在守卫内等待;
  • 反模式:每次导航调接口校验权限、beforeRouteEnter 里摸实例;
  • 懒加载:动态 import 自动分包,重页面独立成包,粒度按切换频率权衡。

守卫链的调试技巧值得单独立一条:遇到"导航没反应"或"莫名其妙被重定向",最快的排查方法是在每个守卫的入口打一行日志(打印目标地址与判定结果),刷新页面看日志链在哪一环断掉或拐弯。比盯着配置文件逐行读快得多。另外守卫代码要保持纯粹——判断归守卫、数据获取归 action、副作用归后置钩子,守卫一旦长出几百行的业务逻辑,它就从"关卡"退化成了"迷宫",这时的正确动作是重构而不是继续往里加条件。守卫也应有对应的测试用例:鉴权规则是安全边界,改一行守卫逻辑跑一遍手工回归是多数团队的现状,把典型路径(未登录、无权限、正常进入)写成路由器级别的单元测试,之后每次动守卫都有护栏,成本低收益稳。

页面切换的机制齐了,下一章看数据反向流动的集中场景——表单。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U