vue3+vite动态路由的实现

之前写过一个一篇关于vue3 cli的文章,状态管理用的vuex,需要参考的可以看下vue3动态路由的实现

现在把项目换成vite,有些地方不同,关于路由的处理也有所改变,所以重新记录一下

需要用到js-cookiepinia

一、登录请求获取路由信息

//点击登录按钮
const submitLogin = async () => {
  const res = await http.post('/login', loginForm.value)
  let token = res.data.data.token
  Cookies.set('token', token)

  let res2 = await http.get('/router')
  if (res2.data.code === 0) {
    let routers = res2.data.data
    if (routers) {
      //存路由信息
      localStorage.setItem('routerData', JSON.stringify(routers))
    } else {
      $message.warning('账号无权限,请联系超级管理员');
    }

    useLoginStore().setIsLogin(true)//存登录状态
    $message.success('登录成功');

    if (redirectUrl.value) {
      await router.push(redirectUrl.value)
    } else {
      await router.push({
        path: "/user"
      });
    }
  }
}

二、渲染路由

router/index.js中定义基本的路由信息

Home组件包含最基本的布局,如菜单,header

import { createRouter, createWebHashHistory } from "vue-router";
import Cookies from "js-cookie";
import { storeToRefs } from "pinia";
import { useLoginStore } from "@/stores/modules/login";
import { useAxiosArrStore } from "@/stores/modules/axiosArr";

import globRouter from "./dealWithRoute";
import Home from "../layout/Home.vue";

const routes = [
  {
    path: "/login",
    component: () => import("../views/login/Login.vue"),
    meta: {
      title: "登录",
    },
  },
  {
    path: "/register",
    component: () => import("../views/login/Register.vue"),
    meta: {
      title: "注册",
    },
  },
  {
    path: "/",
    name: "home",
    component: Home,
    redirect: "/home",
  },
  {
    path: "/:pathMatch(.*)*",
    component: () => import("../components/404.vue"),
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

router.beforeEach((to, from, next) => {
  //asyncRoutesMark:是否加载了路由信息,为false则要请求路由接口
  //isLogin:登录状态
  let { asyncRoutesMark, isLogin } = storeToRefs(useLoginStore());
  let isLoadRouters = asyncRoutesMark.value;
  let token = Cookies.get("token");

  if (token && isLogin.value) {
    if (isLoadRouters) {
      //登录成功后不能通过历史箭头返回登录页面
      if (to.path !== "/login") {
        next();
      } else {
        //如果刚好url变成/login,重置from的path
        next({ ...from, replace: true });
      }
    } else {
      useLoginStore().setAsyncRoutesMark(true);

      //重新请求路由信息,解决刷新页面空白
      useLoginStore().getRouterInfo();

      //渲染动态路由
      if (localStorage.getItem("routerData")) {
       const data = JSON.parse(localStorage.getItem("routerData"));
        const routeList = globRouter(data);
        routeList.forEach((route) => {
          //添加路由
          router.addRoute(route);
        });
      }

      //避免刷新页面后重要数据丢失
      //...可发起一系列请求

      next({ ...to, replace: true });
    }
  } else {
    useLoginStore().setAsyncRoutesMark(false);
    if (to.path === "/login" || to.path === "/register") {
      useAxiosArrStore().clearAxiosArr(); //假如页面有长链接请求,要取消
      next();
    } else {
      next("/login");
    }
  }
});

export default router;

router/dealWithRoute.js 处理路由数据

注意:我的Home.vue布局文件在layout文件下,其它路由组件在views文件下

function globRouter(routeList) {
  let layout = import.meta.glob("../layout/**/*.{vue,tsx}"); //这里指系统布局文件Home.vue
  let dynamicViewsModules = import.meta.glob("../views/**/*.{vue,tsx}"); //普通组件

  routeList.forEach((route) => {
    route.component = dynamicImport(layout, "Home.vue", "../layout");
    if (route.children) {
      route.children.forEach((child) => {
        const component = child.component;
        if (component) {
          child.component = dynamicImport(
            dynamicViewsModules,
            component,
            "../views"
          );
        }
      });
    }
  });
  return routeList;
}
function dynamicImport(dynamicViewsModules, component, path) {
  const keys = Object.keys(dynamicViewsModules);
  const matchKeys = keys.filter((key) => {
    const k = key.replace(path, "");
    const startFlag = component.startsWith("/");
    const endFlag = component.endsWith(".vue") || component.endsWith(".tsx");
    const startIndex = startFlag ? 0 : 1;
    const lastIndex = endFlag ? k.length : k.lastIndexOf(".");
    return k.substring(startIndex, lastIndex) === component;
  });
  if (matchKeys?.length === 1) {
    const matchKey = matchKeys[0];
    return dynamicViewsModules[matchKey];
  } else if (matchKeys?.length > 1) {
    return;
  }
}
export default globRouter;

 三、状态管理

//login.js
import {defineStore} from 'pinia'
import http from "../../api/http";
import Cookies from "js-cookie";

export const useLoginStore = defineStore('login', {
    state: () => ({
        routerData: [],//路由数据
        isLogin: sessionStorage.getItem('isLogin'),//登录状态
        asyncRoutesMark: false,//是否加载了路由信息,为false则要请求路由接口
    }),
    actions: {
        setRouters(data) {
            this.routerData = data;
        },
        setIsLogin(data) {
            this.isLogin = data
        },
        setAsyncRoutesMark(data) {
            this.asyncRoutesMark = data;
        },
        getRouterInfo() {
            http.get('/menu/router').then(res => {
                if (res.data.code === 0) {
                    this.routerData = res.data.data
                    localStorage.setItem('routerData', JSON.stringify(this.routerData))
                }
            })
        },
    }
})
//axiosArr.js
import {defineStore} from 'pinia'

export const useAxiosArrStore = defineStore('axiosArr', {
    state: () => ({
        axiosArr: []
    }),
    actions: {
        setAxiosArr(cancelAjax) {
            this.axiosArr.push(cancelAjax.cancelToken)
        },
        clearAxiosArr() {
            this.axiosArr.forEach(item => {
                item()
            })
            this.axiosArr = []
        },
    }
})

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值