再探Vue,后端动态鉴权模版详解-遇坑排坑

再探Vue,后端动态鉴权模版详解-遇坑排坑

前情回顾

上回说到 上回:超详细vue案例解析 不怕入不了门,vue繁杂的分级目录有个初步认识之后,对怎么入手开发想必已经知晓,那么vue最牛逼的地方-Vue Router: 动态路由鉴权 今天咱也来破一破【正经聊天哈】

要提到动态路由,首先你得知道当你从GitHub拉去的模版项目使用的是静态路由,有必要了解一下 , 以我拉取的Vue-design-pro 【有效结合vue和ant-design组件库真TM香】项目来说 ,上篇末尾其实我已经提到哪里用了静态路由:store目录 ->module目录 -〉permission.js

在这里插入图片描述

这里是静态路由鉴权和页面构造路由树的核心,那么问题来了,具体是怎么操作的? 注意别眨眼

静态路由起飞

注意跟紧思路哈, 本次车速就直接3档起步,请系好安全带:

  1. 登录成功回调里调用:router.push({path:’/’})

  2. 直接被permission.js前置路由守卫: beforeEach拦截 ,拦截方法先校验token 然后校验store里是否有路由,第一次登录必然没有,

    调用store【user状态组件】的multion【你可以理解为controller】 :GetInfo , GetInfo 里获取用户角色信息【作者想要通过先拿角色 在请求mock里的权限数据】,然后对返回的角色信息进行过滤【为按钮级别权限操作鉴权作准备】,之后返回,又调用了store【permission.js】 的GenerateRoutes,并传递了上次请求获取角色信息列表,所讲如下图 这段代码萌新一定很苦逼吧

    稍后我会在动态路由介绍更骚的操作

    if (store.getters.roles.length === 0) {
            // request login userInfo
            store
              .dispatch('GetInfo')
              .then(res => {
                const roles = res.result && res.result.role
                // generate dynamic router
                store.dispatch('GenerateRoutes', { roles }).then(() => {
                  // 根据roles权限生成可访问的路由表
                  // 动态添加可访问路由表
                  router.addRoutes(store.getters.addRouters)
                  // 请求带有 redirect 重定向时,登录自动重定向到该地址
                  const redirect = decodeURIComponent(from.query.redirect || to.path)
                  if (to.path === redirect) {
                    // set the replace: true so the navigation will not leave a history record
                    next({ ...to, replace: true })
                  } else {
                    // 跳转到目的路由
                    next({ path: redirect })
                  }
                })
              })
    
  3. 到了permission.js 好戏才算开始: 将实现事先写好的静态路由asyncRouterMap[router.config.js] 进行改map 和 传入的角色权限 进行过滤【俗称自嗨式鉴权】【作者为了教会我们实属不易】,具体过滤就不cue了,递归有啥讲的

  4. 鉴权完了之后,就是路由渲染了,一样返回后直接调用router.addRoutes 将步骤3保存在状态管理组建的路由 放进去,这是真正意义上的渲染,【vue3.0已经不这么用,md为了区分就是少了个s,防止杠精:逻辑肯定变了,只不过好歹区分度大一点啊】

  5. 之后就是玩哲学三个基本点: who ? where from, where to go, 先去找当前路由的基本信息是否有 redirect参数且有值,还记得登录成功后怎么匹配的吗:

    在这里插入图片描述

    / 从根路由开始匹配,发现有一个redirect 就去重定向:

这一块我被坑了老惨了,还是深陷其中无法自拔的那种,待会在嘲笑我

​ 就是走了上述代码的这个分支:next({ path: redirect })之后就是组建渲染完毕,看到你看到的默认界面了, 问一个问题【笔者开始转型咯】,你们说如果我在generator-router.js 配置的重定向和src目录下permission.js 配置默认跳转路由 优先访问那个?【试一下评论区自信点回复我哦】。


动态路由Action:排坑

现在请忘记刚才所讲的所有内容,和动态路由一点关系没有!【惊不惊喜,意不意外】

动态路由不同之处就在于它是我们后端按照vue路由树识别的规则 进行设计封装后返回给前端,过程繁琐,就直接唠干的 :

  • 参考作者设计的路由,构造后端vue列表

    mock 模版

在这里插入图片描述
后台字段统一对应返回就好,注意:踩坑之路开始

有了数据怎么构造并鉴权了呢? (什么?鉴权?你在想p吃,你是怎么拿到后台数据的,还需要鉴权吗) 【知道为啥让你忘记静态路由吗, 笔者傻傻套用静态的差点没把自己干崩溃】, 所以重点怎么构造路由树:两种方案:【我选了最懒但也是坑最多那种, 虽然我无聊的把另一种写了】

  1. 直接将数据库的权限表 构造对应vue router 参数 返回,就是这么简单,但也是贼恶心的一种

    动态路由的核心入口在:
    在这里插入图片描述

    咋一看那么像静态的, 一点也不! 这里我做了封装,【别瞧不起我low b 的注释,开发vue 没有注释你都像是在做梦一样】

  2. 将返回的数据在登录结束后就保存到本地【够骚气吧】咱们不是探讨安全性问题,就先这样了

  3. 在登录跳转之后 ,依然进入到permisson.js 不过动态路由现在代码是这样滴:

    router.beforeEach((to, from, next) => {
      NProgress.start() // start progress bar
      to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${i18nRender(to.meta.title)} - ${domTitle}`))
      // 获取本地token
      if (storage.get(ACCESS_TOKEN)) {
        console.log('to from next', to, from, next)
        if (to.path === loginRoutePath) {
          console.log('跳转到目标首页')
          next({ path: defaultRoutePath })
          NProgress.done()
        } else {
          // 取出后台放的权限缓存
          const routers = storage.get(ROURTERS)
          if (routers && store.getters.addRouters.length === 0) {
            // request login userInfo
            // generate dynamic router
            const perms = routers || {}
            store.dispatch('ActiveGenerateRoutes', { perms })
            // 根据roles权限生成可访问的路由表
            // 动态添加可访问路由表
            router.addRoutes(store.getters.addRouters)
            console.log('vue 路由', store.getters.addRouters)
            // 请求带有 redirect 重定向时,登录自动重定向到该地址
            // const redirect = decodeURIComponent(from.query.redirect || to.path)
            const redirect = decodeURIComponent(to.path)
            console.log('第一次重定向', redirect)
            if (to.path === redirect) {
              // set the replace: true so the navigation will not leave a history record
              next({ ...to, replace: true })
            } else {
              // 跳转到目的路由
              next({ path: redirect })
            }
          } else {
            console.log('从哪里来:', from)
            console.log('到哪里去:', to)
            next()
          }
        }
      } else {
        if (allowList.includes(to.name)) {
          // 在免登录名单,直接进入
          next()
        } else {
          next({ path: loginRoutePath, query: { redirect: to.fullPath } })
          NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
        }
      }
    })
    

    阅读思路和静态路由类似, 回过头来继续看 动态路由树的构建

  4. vue作者确实周到,也帮我们写好了generator-router.js

    export const generatorDynamicRouterV2 = (res) => {
      const { perms } = res
      const menuNav = []
      const childrenNav = []
      // 后端数据, 根级树数组,  根级 PID
      listToTree(perms, childrenNav, 0)
      rootRouter.children = childrenNav
      // 转成列表形式
      menuNav.push(rootRouter)
      const routers = generator(menuNav)
      routers.push(notFoundRouter)
      return routers
    }
    
    /**
     * 格式化树形结构数据 生成 vue-router 层级路由表
     *
     * @param routerMap
     * @param parent
     * @returns {*}
     */
    export const generator = (routerMap, parent) => {
      return routerMap.map(item => {
        // const { title, show, hideChildren, hiddenHeaderContent, target, icon } = item.meta || {}
        if (item.key === 'System') {
          console.log('前端存在的路由', constantRouterComponents[item.key])
          console.log(item)
        }
        const { title, icon, permission } = item.meta || {}
        const currentRouter = {
          // 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
          // path: item.path || `${parent && parent.path || ''}/${item.key}`,
          path: item.path || `${parent && parent.path || ''}/${item.key}`,
          // 路由名称,建议唯一
          name: item.name || item.key || '',
          // 该路由对应页面的 组件 :方案1
          // component: constantRouterComponents[item.component || item.key],
          // 该路由对应页面的 组件 :方案2 (动态加载)生产使用
          // 在webpack打包方式下import只能动态加载components包下面的组件,其他路径的包只能通过字符串写死方式如:import('@/views/dashborad/Workpalce')否则会出现cant not find module
          // component: (constantRouterComponents[item.component || item.key]) || (() => import(`@/views/${item.component}`))
          // 由此为了避开报错,只能写死在前端,然后动态匹配map的key, 目前只有一种如下方案!!!
          component: constantRouterComponents[item.component],
          // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
          meta: {
            title: title,
            icon: icon || undefined,
            // hiddenHeaderContent: hiddenHeaderContent,
            // target: target,
            permission: permission
          }
        }
        // g代表全部
        // const reg = new RegExp('//', 'g')
        // const curStr = JSON.stringify(currentRouter).replace(reg, '/')
        // const cur = JSON.parse(curStr)
        // // 是否设置了隐藏菜单
        // if (show === false) {
        //   currentRouter.hidden = true
        // }
        // 是否设置了隐藏子菜单
        // if (hideChildren) {
        //   currentRouter.hideChildrenInMenu = true
        // }
        // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
        if (!currentRouter.path.startsWith('http')) {
          currentRouter.path = currentRouter.path.replace('//', '/')
        }
        console.log('当前路由', currentRouter)
        // 重定向
        item.redirect && (currentRouter.redirect = item.redirect)
        // 是否有子菜单,并递归处理
        if (item.children && item.children.length > 0) {
          // Recursion
          currentRouter.children = generator(item.children, currentRouter)
        }
        return currentRouter
      })
    }
    
    /**
     * 数组转树形结构
     * @param list 源数组
     * @param tree 树
     * @param parentId 父ID
     */
    const listToTree = (list, tree, parentId) => {
      list.forEach(item => {
        // 判断是否为父级菜单
        if (item.parentId === parentId) {
          const child = {
            ...item,
            key: item.key || item.name,
            children: []
          }
          // 迭代 list, 找到当前菜单相符合的所有子菜单
          listToTree(list, child.children, item.id)
          // 删掉不存在 children 值的属性
          if (child.children.length <= 0) {
            delete child.children
          }
          // 加入到树中
          tree.push(child)
        }
      })
    }
    
    

    笔者遇到的大部分坑 尽在代码注释中,拿走不谢 😭

  5. 还有单独的两个坑,拿出来

    • 记得在状态组建管理库对外的公共module 进行动态路由组件库替换:
      在这里插入图片描述

      这是替换动态路由时,容易忘记的地方【记得看控制台报错】
      在这里插入图片描述

    这块需要你对vuex 有一定的了解,不多费口舌了,去官网瞅瞅吧

    将构造好的路由树返回后,permission.js 不要再去解析是否有重定向了,因为你动态返回的数据 你怎么知道那个菜单改重定向?就是这一块 被坑哭了都【TMD,不声明一下改处代码是可以变动的】
    const redirect = decodeURIComponent(to.path)

    由此也可以看出,后端的path字段必须是有的!


在啰嗦一句

上面就是动态路由的全部了,基本上按照我上面代码走下去,能保证一定能动态鉴权, 有问题评论区截图反馈,另外以后笔者开始走提问路线了,别问为啥,问就是太寂寞了,没有女票 只好跟各位童鞋唠嗑了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值