vue3 使用addRoute动态添加路由

我们在开发大型系统的时候一般都需要动态添加路由,功能页面一多的话,想必写死这种方法已经完全走不通了

今天就来介绍一下vue3 如何使用addRoute动态添加路由

基本的思路:

通过后端拿到路由数据,遍历路由数据通过router.addRoute一个个的添加,同时将路由数据通过vuex缓存起来,当页面刷新时,从缓存里拿出路由数据再重新添加,同时配合路由守卫来跳页面

遇到的坑:

逻辑不当的情况下,很容易死循环

刷新页面后动态添加的路由页面空白

以下代码同时解决了上面两个所遇到的坑,便再此记录以下

//router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import Layout from "@/layout/index.vue"
import { getMenuNav } from "@/utils/api"
import store from "@/store"

type RouteConfig = RouteRecordRaw & { hidden?: boolean };
const constantRouterMap: Array<RouteConfig> = [
  {
    path: "/",
    component: Layout,
    redirect: '/index',
    name: 'Index',
    meta: {
      title: '首页',
      affix: true,
    },
    children: [{
      path: 'index',
      name: 'Index',
      component: () => import("@/views/index/index.vue"),
      meta: {
        title: '工作台',
        affix: true,
      },
    }]
  },
  {
    path: "/login",
    name: 'Login',
    component: () => import('@/views/user/login.vue'),
    hidden: true,
  }, {
    path: "/404",
    name: "notFound",
    component: () => import('@/views/notfound.vue'),
    meta: {
      title: '页面不存在'
    }
  },
];

const router = createRouter({
  history: createWebHashHistory(process.env.BASE_URL),
  routes: constantRouterMap,
});

let isF = false  //这个是用于判断动态路由是否已经被获取
router.beforeEach(async (to, from, next) => {
  if (to.path == '/login') {
    next();
  }
  const token = localStorage.getItem('token');
  if (!token) {
    next({ path: '/login' });
  } else {
    if (isF) {
      next();
    } else {
      const res = await getMenuNav();
      const addRoute = addData(res.data);//获取动态路由
      // 获取当前默认路由
      const currenRoutes = router.options.routes
      add.forEach((item: any) => {
        // has用于判断当前路由中是否已经具有,避免重复
        const has = currenRoutes.some(it => it.path == item.path)
        if (!has) {
          currenRoutes.push(item)
        }
      })
      // 将404添加进去
      //现在才添加的原因是:作为一级路由,当刷新,动态路由还未加载,路由就已经做了匹配,找不到就跳到了404
      if (currenRoutes[currenRoutes.length - 1].path != '/:catchAll(.*)') {
        currenRoutes.push({ path: "/:pathMatch(.*)", redirect: "/404" });
      }
      // 将新生成的路由替换原路由
      currenRoutes.forEach(item => {
        router.addRoute(item)
      })
      // 将新生成的路由保存到vuex中
      store.dispatch('setRoutes', currenRoutes);
      // 更改控制生成路由次数的值
      isF = true
      // 跳转
      //确保addRoute()时动态添加的路由已经被完全加载上去,不然刷新页面可能会导致空白
      next({ ...to, replace: true });
      // 当然在这一步也可以是去判断一下to.matched是否含有数据,如果没有再触发一次beforeEach
      // if(to.matched.length == 0){
      //     router.push(to.path)
      // }else {next()}

    }
  }

})
export default router;
//store/index.ts
import { createStore } from "vuex";
import http from "@/utils/http";

export default createStore({
  state: {
    userInfo: {
      nickname: '',
      token: '',
      group_id: 0
    },
    allRoutes: [],
  },
  mutations: {
    SET_USERINFO: (state, data) => {
      state.userInfo = data
    },
    SET_ROUTES(state, payload) {
      state.allRoutes = payload
    }
  },
  actions: {
    //登录
    LoginResult({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        http.request({ url: "/user/login", method: 'POST', data: userInfo }).then(
          (res: any) => {
            localStorage.setItem("token", res.data.token);
            localStorage.setItem("nickname", res.data.nickname);
            localStorage.setItem("headimg_url", res.data.headimg_url);
            const userInfo = { nickname: res.data.nickname, token: res.data.token, group_id: res.data.group_id }
            commit('SET_USERINFO', userInfo);
            resolve(res);
          },
          (error: any) => {
            reject(error)
          }
        ).catch((error: any) => {
          reject(error)
        });
      })
    },
    //登出
    LogoutResult({ commit }) {
      return new Promise((resolve, reject) => {
        http.request({ url: "/user/logout", method: 'POST' }).then(
          (res: any) => {
            commit('SET_TOKEN', '');
            localStorage.clear();
            resolve(res);
          },
          (error: any) => {
            reject(error)
          }
        ).catch((error: any) => {
          reject(error)
        });
      })

    },
    setRoutes({ commit }, data) {
      commit('SET_ROUTES', data);
    },
  },
  getters: {
    userInfo: state => state.userInfo,
    allRoutes: state => state.allRoutes,
  },
  modules: {},
});

如果需要再vue组件中使用,就是直接获取vuex中的数据就可以


const tabRoutes = computed(() => {
      return store.getters.allRoutes.filter(route => route.meta);
});
 
// 然后将tabRoutes数组使用v-for在页面上遍历出来就ok了

原创文章,作者:ECHO陈文,如若转载,请注明出处:https://www.luweipai.cn/html/1646221364/