携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
第一种方式:后端控制路由:需要使用vuex
- 后端传递类似这样的数据:{path:'/home',name:'home',component:'Home',icon:'icon1',title:'首页'};
- 我们需要调用这个请求,得到数据,然后放到vuex和localstorage中;
- 什么时候调用这个请求呢?那就是在路由守卫里,但是我们全局的路由守卫每一次路由的变化都会调用,那么我们需要每一次路由的变化都去调用数据吗?当然不需要,我们只需要拿到后就放在vuex或者localStorage中,后面路由再变化的时候,如果有就不需要再调用了,没有再调用。
- 拿到数据以后,我们需要做什么呢?肯定是需要将这部分数据放入我们的
routes里面啊,然后通过router.addRouters(routes)进行路由的替换,以此实现不同的角色拥有不同的路由。
代码:
这里之所以要将获取到的数据同时存在vuex和localStorage(sessionStorage)中,是因为如果仅仅存放在vuex中的话,那么我们进入页面后刷新,就会导致vuex也重新加载了,动态路由数据就会消失,出现页面404。
//登录界面
<template>
<button @click='login'>登录</button>
</template>
<script>
import {login} from '@/server/api'
import { useStore } from 'vuex' // 引入useStore 方法
import { useRouter, useRoute } from "vue-router"
export default {
name:'Login',
setup() {
const store = useStore() // 该方法用于返回store 实例
const router =useRouter() //==>this.$router
const route=useRoute()//this.$route
const login = async () => {
let res = await login();
if(res.code == 200) {
let menuList = res.data.data.menuList; //导航
localStorage.setItem('menuList', JSON.stringify(menuList))
store.commit("setMenuList", menuList);
let routes = res.data.data.routes; //路由
localStorage.setItem('routes', JSON.stringify(routes))
store.commit("setRouters", routes);
let token = res.data.data.token;
localStorage.setItem('token', token);
router.push({path:'/home'});
}
}
}
return {login}
}
</script>
//vuex
import { createStore } from 'vuex'
export default createStore({
state: {
isLoadRouters:false,
routes:[],
menuList:[]
},
mutations: {
setRouters(state,data) {
state.routes = data;
},
setMenuList(state,data) {
state.menuList = data;
},
setIsLoadRouters(state, data) {
state.isLoadRouters = data;
},
},
actions: {}
})
router.addRoute第一个参数是动态路由所在“位置”,是指定“位置”的name属性,第二个参数是要添加的路由信息。
//vue-router
import { createRouter, createWebHistory } from 'vue-router'
import store from '@/store/index'
const routes = [
{
path: "/login",
name: "Login",
component: () => import ("@/views/login/Login.vue"),
meta: {
title: '登录',
}
},
{
path: "/home",
name: "Home",
component: () => import ( "@/views/home/Home.vue"),
}
]
const routerHistory = createWebHistory()
const routers = createRouter({
history: routerHistory,
routes: routes
})
router.beforeEach((to, from, next) => {
let isLoadRouters = store.state.isLoadRouters;
let token = localStorage.getItem('token');
let routes = JSON.parse(localStorage.getItem('routes'));
if (to.path == '/login') {
next()
} else {
//用户已登录
if (token && JSON.stringify(routes) != '[]') {
if (isLoadRouters) {
next() //路由已添加,直接跳转到目标页面
} else {
//解决刷新页面空白,重新加载路由,并跳转到目标页
let route = JSON.parse(localStorage.getItem('routes'))
store.commit('setRouters', route);
store.commit('isLoadRouters', true);
//添加路由
for (let item of route) {
router.addRoute('home', {
path: item.path,
component: () => import(`@/views/${item.component}`),
meta: {
title: item.title,
requiresAuth: true
}
})
}
next({...to,replace: true})
}
} else { //无登录信息,跳转到登录页
store.commit('setMenuList', []);
store.commit('isLoadRouters', false);
localStorage.setItem('token', '');
localStorage.setItem('menuList', JSON.stringify([]));
localStorage.setItem('routes', JSON.stringify([]));
next(`/login`)
}
}
});
export default routers
现在我们成功给路由添加了后台返回的路由数据,最后再让页面的导航栏也添加上就行了!
第二种方式:前端控制路由
第一种方式就存在一个问题:那就是后端不好返回数据,因为后端返回的数据必须是:{path:'xxx',component:xxx,meta:{title:'xxx',icon:'xxx}},就很麻烦,所以就还有一种方式:前端控制路由,你后端只返回一个用户的角色就可以了,前端拿到角色后,自己进行路由的增添修改。
思路:在路由配置里,通过meta属性,扩展权限相关的字段,在路由守卫里通过判断这个权限标识,实现路由的动态增加,及页面跳转;如:我们增加一个role字段来控制角色.
-
登录后返回用户信息,保存在vuex和localStorage(sessionStore)中;
-
在vue-router中配置好静态路由,写好动态路由;
-
页面跳转时,判断在路由守卫里是否已经实现了动态路由;
- 没有实现,就根据用户信息往静态路由中添加动态路由(需要的才添加);
- 实现了,就跳过;
这就是大致思路了,其实看着还是比较简单的。
//登录
import {login} from '@/server/api'
import { useStore } from 'vuex' // 引入useStore 方法
...
setup() {
const store = useStore() // 该方法用于返回store 实例
const login = async () => {
let res = await login();
if(res.code === 200) {
localStorage.setItem('user',res.data.user)
store.commit('setUser',res.data.user)
...
}
}
return {login}
}
//vue-touter
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: "/login",
name: "Login",
component: () => import ("@/views/login/Login.vue"),
meta: {
title: '登录',
}
},
{
path: "/home",
name: "Home",
component: () => import ( "@/views/home/Home.vue")
}
]
export const asyncRoutes = [
{
path: '/jobData',
component: () => import ( "@/views/jobData/JobData.vue"),
name: 'JobData',
meta: {
title: 'JobData',
icon: 'lock',
// 这个意思就是admin、editor这两个角色,这个菜单是可以显示
roles: ['admin', 'editor']
}
},
{
path:'/roleData',
component:()=>import("@/views/roleData/RoleData.vue"),
name:'RoleData',
meta:{
title:'RoleData',
icon:'role',
roles:['admin']
}
}
]
router.beforeEach((to,from,next)=>{
let isLoadRouters = store.state.isLoadRouters;
let token = localStorage.getItem('token');
let user = localStorage.getItem('')
if (to.path == '/login') {
next()
} else {
//用户已登录
if (token && user.role) {
if (isLoadRouters) {
next() //路由已添加,直接跳转到目标页面
} else {
//解决刷新页面空白,重新加载路由,并跳转到目标页
let user = localStorage.getItem('')
store.commit('setUser', route);
store.commit('isLoadRouters', true);
//添加路由
let menuList = [];
for (let item of asyncRoutes) {
if(item.meta.roles.includes(user.role)) {
router.addRoute({
path: item.path,
component: () => import(`@/views/${item.component}`),
meta: {
title: item.title,
requiresAuth: true
}
})
menuList.push(item);
}
}
next({...to,replace: true})
}
} else { //无登录信息,跳转到登录页
store.commit('setMenuList', []);
store.commit('setUser', null);
store.commit('isLoadRouters', false);
localStorage.setItem('token', '');
localStorage.setItem('user', null));
next(`/login`)
}
}
})
const routerHistory = createWebHistory()
const routers = createRouter({
history: routerHistory,
routes: routes
})
export default routers
//vuex
import { createStore } from 'vuex'
export default createStore({
state: {
isLoadRouters:false,
user:null,
menuList:[]
},
mutations: {
setUser(state,data) {
state.user = data;
},
setMenuList(state,data) {
state.menuList = data;
},
setIsLoadRouters(state, data) {
state.isLoadRouters = data;
}
},
actions: {}
})
下面再去根据menuList配一下页面导航栏就行了..
如果有嵌套子路由的话,稍微麻烦一点,不过我想大致原理是差不多的,可能就是添加判断的时候要麻烦一些,后面有空或者遇见了这种情况再说,哈哈~