vben 之 路由守卫初始化应用数据

88 阅读3分钟

vben 之 路由守卫初始化应用数据

不知道你在写前端项目时候,会不会总有一些困惑,就是在什么时候获取用户信息,以及在什么时候获取菜单信息。

让我们从 vben 中学习,路由守卫的初始化应用数据。

为什么使用路由守卫初始化应用数据?

因为用户信息和权限有关,而路由也和权限有关,所以这两个数据,需要在进入页面前进行初始化。

然后一般页面加载的话,都是有个进度条的动画效果,以及判断是否能进入页面这两个功能。

vue-router 的路由守卫可以注册多次,这样,进度条的动画效果,以及判断是否能进入页面这两个功能,就可以分次在路由守卫中处理。

通用守卫

通用守卫负责处理页面加载状态、进度条显示等基础功能。它会记录已加载的页面路径,避免重复执行动画效果。

权限访问守卫

权限访问守卫是核心的安全控制机制,包含以下处理逻辑:

1. 基础路由检查:如登录页等不需要权限验证

如果此时,有 token,且要进入登录页面,如果地址栏的 query 中有 redirect 参数,且 redirect 参数的值不是登录页,那么就跳转 redirect 参数的值,否则就跳转 home 页。 有 query 和 token,这大概是在登录页登录成功后,手动刷新页面会遇到这种情况。

2. 访问令牌验证
  • 没有令牌:没登录,就没有菜单路由
    • 如果路由不需要权限即可访问,那么就直接放行。
    • 如果路由需要权限,那么就跳转登录页,且增加重定向参数,重定向参数的值为当前页面的路径。
3. 如果路由已经生成,那么就直接放行。
4. 如果路由没有生成,那么获取用户信息,角色,菜单,从而生成路由

最后,如果当前路由有重定向参数,那么就跳转重定向参数的值,否则就跳转 home 页。

function setupCommonGuard(router: Router) {
// 记录已经加载的页面
const loadedPaths = new Set<string>();

router.beforeEach((to) => {
 to.meta.loaded = loadedPaths.has(to.path);

 // 页面加载进度条
 if (!to.meta.loaded && preferences.transition.progress) {
   startProgress();
 }
 return true;
});

router.afterEach((to) => {
 // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
 loadedPaths.add(to.path);

 // 关闭页面加载进度条
 if (preferences.transition.progress) {
   stopProgress();
 }
});
}

/**
* 权限访问守卫配置
* @param router
*/
function setupAccessGuard(router: Router) {
router.beforeEach(async (to, from) => {
 const accessStore = useAccessStore();
 const userStore = useUserStore();
 const authStore = useAuthStore();
 // 基本路由,这些路由不需要进入权限拦截
 if (coreRouteNames.includes(to.name as string)) {
   if (to.path === LOGIN_PATH && accessStore.accessToken) {
     return decodeURIComponent(
       (to.query?.redirect as string) ||
         userStore.userInfo?.homePath ||
         preferences.app.defaultHomePath,
     );
   }
   return true;
 }

 // accessToken 检查
 if (!accessStore.accessToken) {
   // 明确声明忽略权限访问权限,则可以访问
   if (to.meta.ignoreAccess) {
     return true;
   }

   // 没有访问权限,跳转登录页面
   if (to.fullPath !== LOGIN_PATH) {
     return {
       path: LOGIN_PATH,
       // 如不需要,直接删除 query
       query:
         to.fullPath === preferences.app.defaultHomePath
           ? {}
           : { redirect: encodeURIComponent(to.fullPath) },
       // 携带当前跳转的页面,登录后重新跳转该页面
       replace: true,
     };
   }
   return to;
 }

 // 是否已经生成过动态路由
 if (accessStore.isAccessChecked) {
   return true;
 }

 // 生成路由表
 // 当前登录用户拥有的角色标识列表
 const userInfo = userStore.userInfo || (await authStore.fetchUserInfo());
 const userRoles = userInfo.roles ?? [];

 // 生成菜单和路由
 const { accessibleMenus, accessibleRoutes } = await generateAccess({
   roles: userRoles,
   router,
   // 则会在菜单中显示,但是访问会被重定向到403
   routes: accessRoutes,
 });

 // 保存菜单信息和路由信息
 accessStore.setAccessMenus(accessibleMenus);
 accessStore.setAccessRoutes(accessibleRoutes);
 accessStore.setIsAccessChecked(true);
 let redirectPath: string;
 if (from.query.redirect) {
   redirectPath = from.query.redirect as string;
 } else if (to.path === preferences.app.defaultHomePath) {
   redirectPath = preferences.app.defaultHomePath;
 } else if (userInfo.homePath && to.path === userInfo.homePath) {
   redirectPath = userInfo.homePath;
 } else {
   redirectPath = to.fullPath;
 }
 return {
   ...router.resolve(decodeURIComponent(redirectPath)),
   replace: true,
 };
});
}