在市面上很多网站都是左侧菜单栏+左侧内容页的样式,同时具有很多个角色,只是左边菜单栏显示的内容不一致罢了,有些人会单独写俩个页面来显示不同的角色,但是这样页面复用性不高,这个时候我们就需要动态路由(有些系统的路由是由后端返回,有些的是由前端提前记录好,本篇讲述的是前端记录好,但是其实后端返回概念是一样的就是需要在返回的结果中获取路由信息而已)
一般公共的路由我们会先存入到 router 的 routes 里面
比如:
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/login",
name: "login",
component: () => import("@/views/login/index.vue"),
},
{
path: "/register",
name: "register",
component: () => import("@/views/register/index.vue"),
},
,
],
});
或者是存入到 basicRoute 中:
const basicRouter = [
{
path: "/login",
name: "login",
component: Login,
},
{
path: "/register",
name: "register",
component: Register,
},
{
path: "/forget",
name: "forget",
component: ForgetPwd,
}
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: basicRouter,
});
然后一般获取动态路由的操作会在登录成功之后,来执行(我这里存入userStore里面的数据就比较简单了,只有身份和token,现实情况肯定不止我这么点信息的)
const res = await loginInAPI(form.value.account, form.value.password);
if (res.data.data.success) {
ElMessage.success("登录成功");
userStore.setUser(res.data.roles, res.data.token);//身份信息 存储到store中
setTimeout(() => {
router.push("/");//登录成功后跳转主页界面
}, 2000);
} else {
ElMessage.error(res.data.data.message);
}
存储成功之后,他这里做了跳转,到主界面,这个时候我们就需要路由守卫来处理
在 router 文件夹下面的 index.ts 下:
router.beforeEach(async (to, from, next) => {
// 判断有没有登录
const userStore = useUserStore();//使用store
const { isLogin, user } = storeToRefs(userStore);//必须使用这种方式
if (to.name == "login") {
next();
return;
}
if (isLogin.value === false) {//如果还没有登录
const res = await setRouter(user.value.roles);//异步请求方式设置 routes
isLogin.value = true;//设置是否登录的值
if (res === true) {
next('/');
} else next(false);
return;
}
next();
});
setRouter 方法
export const setRouter = async (roles: string) => {//导出这个函数
return new Promise((resolve, reject) => {
const myRouter: RouteRecordRaw = getRouter(roles);//获取 routes
router.addRoute(myRouter); //添加路由
resolve(true);
});
};
getRouter 这是在 utils 下面的一个 router.ts 文件 我把路由信息进行了封装放入了进去。需要注意的是 addRoute 时里面的 route 必须是一个对象
import type { RouteRecordRaw } from "vue-router";
const studentRouter: RouteRecordRaw = {};//学生路由
const teacherRouter: RouteRecordRaw = {};//老师路由
export const getRouter = (roles: string): RouteRecordRaw => {
if (identity === 'student') return studentRouter;
else return teacherRouter;
};
这样子就实现了大概的逻辑,但是还是会有点问题,因为当你刷新页面的时候,你缓存的这些就没有了,可以借助 store 把路由信息存储在store里面,但是还是需要进行一步操作:
在 main.ts 中需要这样做,在页面进行刷新时,main.ts 的内容也会重新加载,
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import App from './App.vue'
import router from './router'
import { useUserStore } from './stores/userStore';
// import "./mock/index.ts"; // mock 方式,正式发布时,注释掉该处即可
const app = createApp(App)
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia)
const userStore=useUserStore()
userStore.setRoutes()
这里面的顺序一定不要搞错,一定要是先使用了 pinia 再去使用相关store,userStore.setRoutes()的内容也很简单,调用 router 文件夹下面的 index.ts 中的 setRouter 方法,而且我们在 store 里面使用了 router 里面的函数,位置也必须要在 引入 router 下面使用这个 store
import { ref } from "vue";
import { defineStore } from "pinia";
import { setRouter} from "@/router";
export const useUserStore = defineStore(
"user",
() => {
const user = ref({
roles: "",
token: "",
});
const setUser = (roles: string, token: string) => {
user.value.roles = roles;
user.value.token = token;
};
const setRoutes = () => {
setRouter(user.value.roles);
};
return { user, setUser, setRoutes };
},
{
persist: true,
}
);