vue动态添加路由之避坑指南

你是否遇到了:

  • addRouter后出现白屏
  • 路由守卫出现死循环

踩了很多坑之后,我终于悟到了vue动态添加路由的正确打开方式;

为了设计权限,在前端我们通常采取两种方式
1、在用户登录时获取该用户权限下的路由表,动态生成路由和菜单栏(后端给定路由)
2、在用户登录时获取用户权限,根据权限筛选出需要的路由(前端通过权限筛选路由)

本篇文章采用方式一

关键点:

  • 使用route中addRouter方法动态添加路由
  • 将路由分为
    (1)动态路由 myRouterObj (可从后端获取)
    (2) 静态路由 staticRoutes 没有权限也可以访问的路由
    注:rootRouter 用来组装动态路由。

router index.js页面

import Vue from 'vue'
import Router from 'vue-router'
import home from '@/pages/Home'
import store from '../store/store'
Vue.use(Router)

// 动态路由 模拟后端数据
const myRouterObj = [
  {
    path: '/dashboard',
    name: "dashboard",
    meta: {
      title: '系统首页'
    },
    component: "Dashboard"

  },
  {
    path: '/table',
    name: "commonTable",
    meta: {
      title: '表格'
    },
    component:
      "CommonTable"

  },
  {
    path: '/calendar',
    name: "calendar",
    meta: {
      title: '日历'
    },
    component: "Calendar"
  }
]

// 静态路由 没有权限也可以访问的路由
export const staticRoutes = [
  {
    path: "/login",
    name: "login",
    component: () => import(
      "@/pages/Login.vue"
    ),
  }
];

// 根路由
const rootRouter = {
  path: '/',
  name: 'home',
  component: home,
  redirect: '/dashboard',
  children: []
};

export const generatorDynamicRouter = () => {
  return new Promise((resolve, reject) => {
	// myRouterObj 这里直接写在页面中了,实际应用中我们需要进行ajax请求获取
    const routesForHis = generatorForHis(myRouterObj);
    // routesForHis .push(notFoundRouter);  // 可以定义404nofoun单页面路由
    rootRouter.children = routesForHis;
    resolve(rootRouter);
  });
};

export const generatorForHis = (routeMap) => {
  return routeMap
    .map(item => {
      const currentRouter = {
        path: item.path,
        // 路由名称,建议唯一
        name: item.name,
        // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
        meta: item.meta,
        // 该路由对应页面的 组件 (动态加载 @/pages/ 下面的路径文件)
        component: () => import(`@/pages/` + item.component + '.vue')
      };

      // 子菜单,递归处理
      if (item.children && item.children.length > 0) {
        currentRouter.children = generatorForHis(item.con);
        if (currentRouter.children === undefined || currentRouter.children.length <= 0) {
          delete currentRouter.children;
        }
      }
      return currentRouter;
    })
    .filter(item => item);
};

// 开始创建路由时
const router = new Router({
  mode: 'history',
  base: '/',
  linkActiveClass: "active",
  routes: staticRoutes
})

router.beforeEach(async (to, from, next) => {
  var isLogin = store.state.isLogin;
  if (to.path == "/login") {
    next()
  } else {
    if (isLogin) {
      if (store.state.allowRouters && store.state.allowRouters.length > 0) {
      	// 路由的出口 判断addRoute是否已经完成 避免路由守卫进入死循环
        next()
      } else {
        const allowRouters = await store.dispatch("GENERATE_ROUTES_DYNAMIC", "aa")
        if (allowRouters.children.length == 0) {
          return false;
        }
        if (allowRouters) {
          next({ ...to, replace: true })
        }
        return false;
      }
    } else {
      next('/login')
    }
  }
})
export default router;

store store.js文件

import Vue from 'vue'
import Vuex from 'vuex'
import { generatorDynamicRouter } from '@/router'
import { default as router, staticRoutes } from '@/router';
Vue.use(Vuex)

let isLogin = ''
try {
  if (localStorage.isLogin) {
    isLogin = JSON.parse(localStorage.isLogin)
  }
} catch (e) { }

export default new Vuex.Store({
  state: {
    isLogin: isLogin,
    allowRouters: [],
  },
  mutations: {
    login(state) {
      state.isLogin = true;
      localStorage.isLogin = true;
    },
    SET_ROUTERS(state, data) {
      state.allowRouters = data;
    },
  },
  actions: {
  	// 产生动态路由
    GENERATE_ROUTES_DYNAMIC({ commit }, data) {
      return new Promise(resolve => {
        generatorDynamicRouter()
          .then((routes) => {
            const allowRoutes = routes.children || [];
            // 添加到路由表
            console.log('allowRoutes: ', allowRoutes);
            router.addRoute(routes);
            commit('SET_ROUTERS', allowRoutes);
            resolve(routes);
          })
          .catch(err => {
            console.error('generatorDynamicRouter', err);
          });
      });
    },
  }
})

关于next({ ...to, replace: true })的理解
很多人在使用动态添加路由addRoutes()会遇到下面的情况:

  • 问题:在addRoutes()之后,第一次访问被添加的路由会白屏。
  • 原因:在addRoutes()之后,立刻访问被添加的路由时,addRoutes()没有执行结束,因而找不到刚刚被添加的路由导致白屏。
  • 解决方案:需要重新访问一次路由才行。

该如何解决这个问题 ?
此时就要使用next({ ...to, replace: true })来确保addRoutes()时动态添加的路由已经被完全加载上去。

关于next({ …to, replace: true })

  1. replace: true只是一个设置信息,告诉VUE本次操作后,不能通过浏览器后退按钮,返回前一个路由。
  2. next({ ...to })的执行很简单,它会判断:
    如果参数to不能找到对应的路由的话,就再执行一次路由守卫(beforeEach((to, from, next))直到其中的next({ ...to})能找到对应的路由为止。

next({ ...to, replace: true })可以保证addRoutes()已经执行完成,路由中已经有我们后来添加进去的用来路由了。
找到对应的路由之后,接下来将前往对应路由,并执行路由守卫(beforeEach((to, from, next)),因此需要用代码来判断是否可以该入改路由(本文的代码根据store中的allowRoutes的长度是否大于零来确定是否进行next()),如果是,就执行next()放行。

如果守卫中没有正确的放行出口的话,会一直next({ ...to})进入死循环 !!!

因此你还需要确保在当addRoutes()已经完成时,所执行到的这一次路由守卫beforeEach((to, from, next)中有一个正确的next()方向出口。

以下就是上述内容的伪代码,敲重点

if (store.state.allowRouters && store.state.allowRouters.length > 0) {
   // 路由的出口 addRoute已经完成 避免路由守卫进入死循环
   next()
} 
else{
	router.addRoute('要添加的路由');
	// 保证addRoute已经完成,并导航到对应的路由
	next({ ...to, replace: true })
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值