项目基本目录
1.首先定义初始默认的路由routes(router.js文件),vue文件使用import引入可以按需加载
import {
createRouter,
createWebHashHistory
} from "vue-router";
import store from '../store/index.js'
const routes = [{
path: "/login",
component: () => import("../view/Login/index.vue"),
children: [],
meta: {
title: '登录页',
hideMenu: true, //加入hideMenu属性,不展示在侧边栏
},
name: "Login",
},
{
path: "/",
component: () => import("../view/Home/index.vue"),
meta: {
keepalive: true,
title: "主页",
},
name: 'Home',
// hideMenu: true,//不展示在侧边栏
children: [],
redirect: '/index'
},
]
2.在store的login.js模块写入调用后端数据的函数(写在vuex的action对象中,方便全局异步调用)
Vuex 允许我们将 store 分割成模块(module),比如登录模块,项目各个业务不相关的模块。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块(具体可以看主页另一篇博客)
3.执行addRoutes函数获取动态路由 (在router.js文件,点击登录成功后,在全局路由守卫去判断是否登录成功,再调用addRoutes函数)
(1)全局路由守卫逻辑具体可看注释,好处是进入项目之后,刷新页面****路由守卫会拦截跳转,可以重新执行addRoutes()获取路由,先获取动态路由,再执行跳转,不然页面会报本地路由找不到(一个小坑)
(代码如下)
router.beforeEach(async (to, from, next) => { //路由守卫
if (store.state.login.token) { //存在token
if (to.path == '/login') { //存在token,如果想跳转到登录页,默认有token跳转进项目首页
next({
path: '/'
})
} else {
//如果存在token,跳转进项目页面,则判断当前后端返回的路由有无长度
//或者有无即将跳转路由的name
if (store.getters['login/getRoutes'].length || to.name != null) {
next() //有的话直接跳转
} else { //不满足条件的话,重新请求后端动态路由数据
await addRoutes(); //addRoutes()必须加入await!!!!等待逻辑执行完毕获取路由
// 如果 addRoute 未完成,路由守卫会一层一层的执行执行,不加next()可能导致卡死!
//直到 addRoute 完成,找到对应的路由
next({
...to,
replace: true
})
}
}
} else {
if (to.path == '/login') {
next()
} else {
next('/login')
}
}
})
后端返回的格式
(2)(重点在****这,前面的步骤都可以不是重点)Vite使用import.meta.glob动态导入view文件夹所有前端页面,并调用addRoutes()函数执行获取动态路由数据并做处理(代码如下),主要作用是替换掉后端返回的component格式,再addRoute进路由表(看不懂的可以看代码注释或者可以搬进自己的项目打印看看每一步获取的数据)
let asyncRoutes = [] //定义数组接收后端返回的路由
const routeAllPathToCompMap =import.meta.glob(`../view/**/*.vue`);
//**为通配符,vite不支持require导入方式,故用import.meta.glob(vite动态导入)
/*import.meta.glob
* 该方法匹配到的文件默认是懒加载,通过动态导入实现,构建时会分离独立的 chunk,是异步导入,返回的是 Promise
* /*import.meta.globEager
* 该方法是直接导入所有模块,并且是同步导入,返回结果直接通过 for...in循环就可以操作
*/
async function addRoutes() {
await store.dispatch('login/getNewRoutes').then((res) => { //获取后端返回的动态路由
if (res.data && res.data.length) {
// let homeRouteChildren = [];
asyncRoutes = res.data;
/*
* 1。拿到处理完毕home的children,最终替换掉原来的children,给菜单渲染提供支持
* 2.通过递归,调用router.addRoute,把每一项route插到对应的父route下
*/
//服务端配置了路由,但前端还没添加对应文件的路由列表,内容是路由的component值(服务端的)
// const unForList = ['']
const homeChildren = routes[1].children;
const dfs = (parentRouteName = 'Home', asyncRoutes = [], originRouteChildren = []) => {
if (!Array.isArray(asyncRoutes)) return [];
asyncRoutes.forEach(route => {
// if (unForList.includes(route.component)) return;
/**后端返回来的路由component是字符串,如component: "view/Index/index.vue",
* 前端需要把component: "view/Index/index.vue" 转化为组件对象
* component:() => import("/src/view/Index/index.vue")
**/
route.component = routeAllPathToCompMap[`../${route.component}`];
// route.component = () => import(`../${route.component}`);
const routeChildren = route.children;
router.addRoute(parentRouteName, route);
route.children = dfs(route.name, routeChildren)
originRouteChildren.push(route)
})
return originRouteChildren
}
// homeRouteChildren = dfs(asyncRoutes)
dfs('Home', asyncRoutes, homeChildren)
}
});
}
最终转化完成,路由数据格式如下
动态路由到此完成
版权归原作者 祥武的哥哥 所有, 如有侵权,请联系我们删除。