之前写过一个一篇关于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: "/home"
});
}
}
}
二、渲染路由
在router/index.js中定义基本的路由信息,动态生成的路由默认放在Home组件。
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 dealWithRoute 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 routerData = JSON.parse(localStorage.getItem("routerData"));
dealWithRoute(routerData);
}
//避免刷新页面后重要数据丢失
//...可发起一系列请求
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动态添加路由
Vite 是一个基于 ES Module 的前端构建工具,它使用了静态导入的概念。在静态导入中,所有的导入路径都应该是静态字符串,不应该包含任何动态变量或表达式。
因为我们的路由是动态添加的,在使用import函数时,要确保导入的表达式是一个静态字符串component: () => import(path)
import router from "./index";
import { RouterView } from "vue-router";
const dealWithRoute = (data, parent = "home") => {
if (data) {
for (let item of data) {
if (item.children && item.children.length > 0) {
router.addRoute(parent, {
path: item.path,
name: item.path.split("/")[1],
component: RouterView,
meta: {
title: item.name,
requiresAuth: true,
},
});
dealWithRoute(item.children, item.path.split("/")[1]);
} else {
//特殊情况特殊处理
item.component_ = item.component.replace(".vue", "");
let filePath = `../views/${item.component_}.vue`;
router.addRoute(parent, {
path: item.path,
name: item.path.split("/")[1],
component: () => import(filePath),
//component: () => import(`../views/${item.component}`),//vue cli 写法
meta: {
title: item.name,
requiresAuth: true,
},
});
}
}
}
};
export default dealWithRoute;
三、状态管理
//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 = []
},
}
})