之前写过一个一篇关于vue3 cli的文章,状态管理用的vuex,需要参考的可以看下vue3动态路由的实现
现在把项目换成vite,有些地方不同,关于路由的处理也有所改变,所以重新记录一下
需要用到js-cookie、pinia
一、登录请求获取路由信息
//点击登录按钮
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 = []
},
}
})