vue3 + ts +pinia+element-plus+mock 项目---动态路由+用户权限路由篇

动态路由+用户权限路由篇

1、实现思路

       实现动态路由前提是服务端做好Router路由表的返回数据格式,一般分为两种情况(本人见识少,目前只遇到这两种)一是服务端返回客户端登录用户的权限ID数组,客户端通过权限ID修改对应路由的hidden属性选择是否展示,同时在router.beforeEach中判断好每次路由跳转的目标地址是否符合权限信息,防止通过地址栏输入方式跳转;二是前端配置好login,home等基础通用路由,再由服务端返回完整路由表,通过解析路由数据,动态添加至路由表中。

2、本文主要讲述上述第二种方式如何实现动态路由

步骤:从login页跳转home时,获取数据存储在pinia中,beforeEach前置钩子解析pinia中的数据,动态添加至路由表。

(1)配置:vue3 +typeScript+pinia+mock

基础配置如何引入就不过多阐述,进入代码部分。

Router基础路由表

{
    path: "/",
    redirect: "/login"
  },
  {
    path: "/login",
    name: "Login",
    meta: { title: "登录", },
    component: () => import("../views/login/LoginView.vue"),
  },
  {
    path: "/home",
    name: "home",
    redirect: "/home/homepage",
    component: () => import("../views/home/HomeView.vue"),
    children: [
      {
        path: "/home/homepage",
        name: "homepage",
        meta: { title: "首页", icon: "icon-shouye", hidden: false, role: ['root', 'user'] },
        component: () => import("../views/homepage/index.vue"),
      }
    ]
  }

mock数据

 router: [
    {
      path: "/home/echarts",
      name: "echarts",
      meta: { title: "图表", icon: "icon-tubiao-zhexiantu", hidden: false },
      component: "/echarts/index",
    },
    {
      path: "/home/error",
      name: "error",
      meta: { title: "错误页", icon: "icon-cuowuyemian", hidden: false },
      redirect: "/error/404",
      component: '/error/index',
      children: [
        {
          path: "/home/error/404",
          name: "notfound",
          meta: { title: "404", icon: "", hidden: true },
          component: "/error/404",
        }
      ]
    },
    {
      path: "/home/table",
      name: "table",
      meta: { title: "表格", icon: "icon-table", hidden: false },
      component: "/table/index",
    },
    {
      path: "/home/edit",
      name: "edit",
      meta: { title: "编辑", icon: "", hidden: true },
      component: "/table/edit",
    },
    {
      path: "/home/guide",
      name: "guide",
      meta: { title: "国际化", icon: "icon-guojihua", hidden: false },
      component: "/guide/index",
    },
    {
      path: "/home/personal",
      name: "personal",
      meta: { title: "个人中心", icon: "icon-wode", hidden: false },
      component: "/personnal/index",
    },
    {
      path: "/home/system",
      name: "system",
      meta: { title: "系统设置", icon: "icon-xitongshezhi", hidden: false },
      component: "/system/index",
    }]

(2)获取数据,获取数据采用axios,获取mock定义好的模拟数据。

import {  useMenuStore } from '@/stores'
const MenuStore = useMenuStore()

//下方代码本人是定义在login页面的点击登录事件的处理函数中进行执行
const { data: res } = await loginApi(formData)
MenuStore.setMenuList(res.data.router) 
// 路由信息存储在pinia中,同时因为pinia中使用persist插件实现数据持久化,
本质也是在local storage中存了备份,persist插件使用具体可以自行搜索

pinia中useMenuStore配置,还需自行配置Store下index.ts,将useMenuStore引入。

import useMenuStore from './modules/menuList';
export { useMenuStore}
import { defineStore } from "pinia";

const useMenuStore = defineStore('useMenuStore', {
  state: () => {
    return {
      menuList: '',
    };
  },
  actions: {
    setMenuList(routes: any) {
      this.menuList = routes;
    },
    clearMenuList() {
      this.menuList = '';
    },
  },
  persist: {
    enabled: true,
    strategies: [
      { key: 'routes', storage: localStorage, paths: ['menuList'] },
    ],
  },

});

export default useMenuStore

(3)处理数据,从login页跳转到home页过程中,数据已经获取到了,接下来就是到router.beforeEach中处理相关数据。

import { addRoutes, clerRoutes } from '@/utils/asyncRoute';
// 前置路由守卫,添加动态路由
let registerRouteFresh1 = true;  //记录路由动态加载的状态
let registerRouteFresh2 = true;

router.beforeEach(async (to, from, next) => {
  if (to.name !== 'Login' && !userStore.token) {
    next({ name: 'Login' });
  } else {
    if (to.name === 'Login') {
      userStore.clearToken();
      userStore.clearUser();
      clerRoutes(menuStore, router);
    }

    // 从login 页面进入到homepage  如果你的数据是加载login页面获取,该if可不要
    if (from.name === 'Login' && registerRouteFresh1) {
      addRoutes(menuStore, router);
      next({ ...to, replace: true });  
     //{ ...to, replace: true }  因为动态添加路由时,可能会存在添加未完成时路由发生跳转
       此时通过上述代码可重定向到beforeEach执行之前,再次执行加载
      registerRouteFresh1 = false;  
    }
    // 本地刷新的时候执行  
    if (!from.name && registerRouteFresh2) {
      addRoutes(menuStore, router);
      next({ ...to, replace: true });
      registerRouteFresh2 = false;
    } else {
      next();
    }
  }

})

通过上面的钩子函数可以实现解析路由数据,添加至路由表的过程,下述讲解方法的具体实现方式

(4)解析添加路由、清除路由的具体实现

解析:router4.0主要通过addRoute的API。

// 添加动态路由
const addRoutes = (menuStore: any, router: any) => {
  if (menuStore.menuList && menuStore.menuList.length > 0) {
//首先判断传入的 store中是否时存在数据的,一般在获取数据时就会判断数据是否正确,且pinia增加持久化,故此地也可不要if判断
    const routesData = JSON.parse(JSON.stringify(menuStore.menuList));
//深拷贝数据,慎用,上线之后项目会报错,大致是因为循坏加载constructor 
    const views = import.meta.glob('../views/**/*.vue');
//vue3 新写法,目的是为了后续处理数据中的组件引入路径
    recursiveRoutes(routesData, views);
//路由处理函数,下述附代码
    routesData.forEach((item: any) => {
//路由数据解析完成,开始添加到本地路由表中
      router.addRoute('home', item);
    });
  }
  router.addRoute({
//在所有的路由动态添加完毕后,需再次添加404页面,注意vue3新写法
    path: '/:pathMatch(.*)*',
    name: '404',
    component: () => import('@/views/error/404.vue'),
    meta: {
      title: '无法找到该页面',
      layout: false,
    },
  });
};

处理数据函数实现。

// 递归遍历路由数据
const recursiveRoutes = (tree: any[], views: any) => {
  return tree.map((node) => {
    const tempNode = node;
    if (tempNode.component) {
     // 修改每一个路由的组件引入路径
      tempNode.component = views[`../views${tempNode.component}.vue`]
    }
    if (tempNode.children && tempNode.children.length > 0) {
     // 存在嵌套路由时,递归调用该函数处理
      recursiveRoutes(tempNode.children, views);
    }
    return tempNode;
  });
};

退出时清除路由函数。

// 清除动态路由
const clerRoutes = (menuStore: any, router: any) => {
  if (menuStore.menuList && menuStore.menuList.length > 0) {
    menuStore.menuList.forEach((item: any) => {
      router.removeRoute(item.name);
    });
    menuStore.clearMenuList();
//直接调用menus tore中定义好的action函数
  }
};

至此,动态路由数据基本就可以成功添加在本地的路由表中了。至于渲染,采用的是element-plus中的el-menu组件,具体见代码:

//思路  :v-if 判断是否存在嵌套模式,不存在直接走 v-else 渲染普通路由

 //存在  首先渲染嵌套路由的父路由,通过获取父路由的children 来循环渲染子路由

你可能感兴趣的:(vue3,+typescript,vue,typescript,elementui)