后端nodejs返回路由,前端vue动态添加路由的过程

之前我是将路由直接写在前端,最近调整了一下,采用后端返回路由的形式,这里整理一下我实现的过程。

1、后端接口

首先先写好后端接口,这里因为没有涉及到业务逻辑和数据处理,所以我直接在routes中写了。

这里要注意如果路由包含children,则一级路由一定要加上路由名称name否则后续前端添加路由不会添加上。

权限我是通过permission这个包含用户角色的数组来表示,前端根据这个判断用户是否有权限。

MenuListRouter.get('/api/adminApi/menulist', (req, res)=>{
    res.send({
        data: [
            {
                path: '/',
                redirect:'/index',
                meta:{
                    permission:[1, 2]
                }
            },
            {
                path: '/index',
                name: 'index',
                component: 'Home/Home',
                meta:{
                    label: '首页',
                    icon: 'HomeFilled',
                    permission:[1, 2]
                }
            },            
            {
                path: '/user',
                name: 'user',
                children:[
                    {
                        path: '/user_manage',
                        name: 'userManage',
                        component: 'user_manage/index',
                        meta:{
                            label: '用户列表',
                            permission:[1]
                        }
                    },
                ],
                meta:{
                    label: '用户管理',
                    icon: 'UserFilled',
                    permission:[1]
                }
            }                     
        ]
    })
});

module.exports = MenuListRouter;

 2、前端动态添加路由

从后端接口获取数据,并将component转换为真实组件,这里要通过import.meta.glob把对应views文件夹下的所有vue文件先导入进来。

// 获取当前用户对应的路由
const getRoutes = async () => {
  // 当前用户角色
  let role = store.state.userInfo.role;
  // 存储路由信息
  let routeArr: any[] = [];
  const res = await getRouter();
  let resArr = res.data.data;
  resArr.map((item: any) => {
    // 判断当前用户拥有的权限
    if (item.meta.permission.includes(role)) {
      if (!item.children) {
        // 判断是否为重定向'/'
        item.component ? getComponent(item) : item;
        routeArr.push(item);
      } else {
        item.children.map((childItem: any, index: number) => {
          item.children[index] = getComponent(childItem);
          routeArr.push(item);
        });
      }
    }
  });
  // 将路由信息保存到store中
  store.commit("setMenu", routeArr);
  return routeArr;
};
//从文件系统导入
const modules = import.meta.glob(["./views/**/*.vue"]);

//将component转为真实组件
const getComponent = (item: any) => {
  let component = `./views/${item.component}.vue`;
  item.component = modules[component];
  return item;
};

 通过router.addRoute()可以动态添加路由,这里动态添加之后可以通过router.getRoutes()查看所有的路由信息,会发现添加进来的路由都变成了一级路由,即使是子路由也会被放在一级路由上。

// 动态添加路由
const addRoute = async () => {
  const routeArr = await getRoutes();
  routeArr.forEach((item: any) => {
    router.addRoute("mainbox", item);
  });
  store.commit("changeIsGetterRouter", true);
};

 路由守卫这里要注意一些细节问题,比如next()和next('/login')的区别等。

router.beforeEach(async (to, from, next) => {
  // 未授权
  if (!localStorage.getItem("token")) {
    // 这里不能只写next('/login'), 不然不会放行,只有next()会放行,其他的都是中断当前导航,执行新的导航
    if (to.path == "/login") {
      next();
    } else {
      next("/login");
    }
  } else {
    // 已授权(已登录)
    if (!store.state.isGetterRouter) {
      await addRoute();
      // 确保动态添加的路由已经被完全加载上去,解决添加完路由后第一次访问页面出现白屏的问题(直接放行就会出现这种问题)
      next({
        ...to,
        replace: true, //不能通过浏览器后退按钮,返回前一个路由
      });
    } else {
      // 可以直接放行,如果路由不存在则匹配404
      next();
    }
  }
});

 3、侧边栏菜单数据展示

这里要注意图标要用component动态展示,外层还要套上el-icon,不然出来的图标超级大,还有因为引入的图标不知道具体是什么,所以要全局注册图标组件。

      <div v-for="item in sideMenuList">
        <el-menu-item v-if="!item.children" :index="item.path">
          <el-icon>
            <component :is="item.meta.icon" class="icon" />
          </el-icon>
          <span>{{ item.meta.label }}</span>
        </el-menu-item>

        <el-sub-menu v-else :index="item.path">
          <template #title>
            <el-icon>
              <component :is="item.meta.icon" class="icon"></component>
            </el-icon>
            <span>{{ item.meta.label }}</span>
          </template>

          <el-menu-item v-for="data in item.children" :index="data.path">
            {{ data.meta.label }}
          </el-menu-item>
        </el-sub-menu>
      </div>

 4、实现过程中的一些小问题及解决方法

a、每一次页面刷新都会重新加载一次路由,该如何解决?

把菜单信息放到vuex-persistedstate中持久化存储,使用vuex-persistedstate插件可以实现vuex中数据的持久化存储,可以通过paths指定存储的数据。(我下载的是4.1.0版本的,现在有新的版本,但我觉得这个版本的用起来简单点)

import createPersistedState from "vuex-persistedstate";

plugins: [createPersistedState({
   paths: ["isCollapsed", "userInfo", "menu"]  //指定持久化存储的数据
})],

b、在切换不同角色测试他们的菜单是否一样时,发现从一个角色切换到另一个角色,不刷新只是退出登录,还是前一个角色的菜单栏, 只有刷新了才会切换,是什么原因呢?

因为菜单持久化存储了,所以不刷新menu的值是一样的,刷新之后store中的 是否获取路由 为false,会重新去获取一遍路由,所以可以在退出登录函数中把 是否获得路由 设为false,这样登录进去会重新获取路由信息,就不会出错了。

c、点了退出登录menu信息还存在,会不会出现在输入menu中存在的路由后进入某个页面呢?

不会,因为退出登录时已经移除了token,经过路由守卫会被要求去到login页面,无论输入什么都结果都是一样的。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值