首页 > 其他分享 >vue动态添加路由之避坑指南

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

时间:2022-10-20 22:36:37浏览次数:59  
标签:vue const next isLogin state 路由 之避 store

vue动态添加路由之避坑指南:https://www.it610.com/article/1517505460574420992.htm

 

你是否遇到了:

  • 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 })
}

标签:vue,const,next,isLogin,state,路由,之避,store
From: https://www.cnblogs.com/bydzhangxiaowei/p/16811566.html

相关文章

  • VueX和组件传参
    组件的传参父传子props子传父事件$emit引用父组件$parent引用子组件$children/$refs引用根组件$root最大组件事件bus传参(跨层级访问数据)(非父子关系)创......
  • .Net Api + Vue前后端分离项目中的jwt令牌应用
    1.Startup里面配置JWT认证方案//认证方案services.AddAuthentication(option=>{option.DefaultAuthentic......
  • Vue3.x+element-plus+ts踩坑笔记
    闲聊前段时间小颖在B站找了个学习vue3+TS的视频,自己尝试着搭建了一些基础代码,在实现功能的过程中遇到了一些问题,为了防止自己遗忘,写个随笔记录一下嘻嘻项目代码git地址:......
  • vue.config.js 常用的属性
    //vue.config.js文件是脚手架的配置文件const{defineConfig}=require("@vue/cli-service");module.exports=defineConfig({transpileDependencies:true,......
  • html项目引入element-ui和vue【转载】
    本地引用element-ui和vue<linkrel="stylesheet"type="text/css"href="./element.css"><scriptsrc="./js/vue.js"></script><scriptsrc="./js/element.js"></scrip......
  • 防火墙基础之BGP路由技术安全防护内网安全​
    防火墙基础之BGP路由技术安全防护内网安全​原理概述:​防火墙(英语:Firewall)技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备,帮助计算机网络于其内、外网之间构建......
  • Vue中点击空白区域让某个div消失
    需求:有个下拉框,下拉框选择自定义时,有个树状列表选择,再点击空白区域,树消失选择自定义会有个树  然后就是点击空白区域让树消失 <divclass="page"@click="clic......
  • vue使用WebUploader做大文件的分块和断点续传
    ​ 这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数下面直接贴代码吧,一些难懂的我大部分都加上......
  • 一种简单的视图层数据查询模块数据流--视图、路由、API
    背景简介实现前台项目的搜索页时,通常会将查询参数直接传给路由并显示在顶部的地址。params参数可以路径的一部分,因此可以理解;不过保留query的目的暂不清楚,对SSR模......
  • 前端Vue2-Day54
    ToDoList案例:组件间通信:父组件给子组件传参:父组件绑定值至子标签,子组件利用props接收。子组件给父组件传参:父组件绑定函数至子标签,子组件props接收函数,并设置methods进......