// router/routes.js // 堆代码 duidaima.com import Home from "../views/Home.vue"; const routes = [ { path: "/", name: "Home", component: Home, meta: { needLogin: false, // 不需要登录 title: "首页", }, }, { path: "/about", name: "About", component: () => import(/* webpackChunkName: "about" */ "../views/About.vue"), // 路由懒加载 meta: { needLogin: true, // 需要登录 title: "关于", roles: ["admin", "manage"], // 该页面只有admin和普通管理员才能进入 }, }, { path: "/nopermission", // 没权限就进入该页面 name: "NoPermission", component: () => import( /* webpackChunkName: "nopermission" */ "../views/NoPermission.vue" ), // 路由懒加载 meta: { needLogin: true, // 需要登录 title: "暂无权限", }, }, { path: "/userlist", name: "UserList", component: () => import(/* webpackChunkName: "userlist" */ "../views/UserList.vue"), // 路由懒加载 meta: { needLogin: true, // 需要登录 title: "用户管理", roles: ["admin"], // 该页面只有admin才能进入 }, }, { path: "/login", name: "Login", component: () => import(/* webpackChunkName: "login" */ "../views/Login.vue"), // 路由懒加载 meta: { needLogin: false, // 不需要登录 title: "登录", }, }, ]; export default routes;实例化 Router
// router/index.js // vue2 写法 import VueRouter from "vue-router"; import routes from "./routes" const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes, }); // vue3 写法 import { createRouter, createWebHistory } from "vue-router"; import routes from "./routes" const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes, });定义路由拦截鉴权逻辑
// router/index.js // vue2和vue3通用 router.beforeEach(async (to, from, next) => { // 如果需要登录 if (to.meta.needLogin) { // 获取token const token = localStorage.getItem("token"); // 如果有token 则直接放行 if (token) { // 获取用户信息,从store里面获取 let userInfo = store.getters["getUserInfo"]; // 如果没有用户信息就获取用户信息 if (!userInfo) { userInfo = await store.dispatch("getUserInfoAction"); } // 如果页面需要权限,并且用户角色不满足则去无权限提示页 if (to.meta.roles && !to.meta.roles.includes(userInfo.role)) { return next("/nopermission"); } next(); } else { // 否则去登录页 next("/login"); } } else { // 不需要登录则直接放行 next(); } }); // 修改标题的工作可以放在全局后置守卫 router.afterEach((to, from) => { if (to.meta.title) { document.title = to.meta.title; } });我们再来看看 store 的逻辑
import { createStore } from "vuex"; export default createStore({ state: { userInfo: null, }, getters: { getUserInfo: (state) => state.userInfo, }, mutations: { setUserInfo(state, payload) { state.userInfo = payload; }, }, actions: { async getUserInfoAction({ commit }) { // 模拟后端获取用户信息的api const getUserInfoApi = () => { return Promise.resolve({ role: "manage", name: "jack" }); // 假设角色为 manage }; const userInfo = await getUserInfoApi(); commit("setUserInfo", userInfo); return userInfo; }, }, });使用
import router from "./router"; // vue2写法 new Vue({ router, render: (h) => h(App), }).$mount("#app"); // vue3写法 const app = createApp(App); app.use(router).mount("#app");在没登录的情况下,去about和userlist页面都会重定向到登录页。给本地添加token 模拟登录完成后,因为用户信息角色是 manage,所以去 about 是没问题的,去 userlist 则会重定向到没有权限页面。
import Home from "../views/Home.vue"; const routes = [ { path: "/", name: "Home", component: Home, meta: { title: "首页", }, }, { path: "/nopermission", // 没权限就进入该页面 name: "NoPermission", component: () => import( /* webpackChunkName: "nopermission" */ "../views/NoPermission.vue" ), // 路由懒加载 meta: { title: "暂无权限", }, }, { path: "/login", name: "Login", component: () => import(/* webpackChunkName: "login" */ "../views/Login.vue"), // 路由懒加载 meta: { title: "登录", }, }, ]; export default routes;实例化 Router
// router/index.js // vue2 写法 import VueRouter from "vue-router"; import routes from "./routes" const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes, }); // vue3 写法 import { createRouter, createWebHistory } from "vue-router"; import routes from "./routes" const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes, });
// router/index.js // vite创建的项目 使用这种方法实现动态加载 const modules = import.meta.glob("../views/*.vue"); // 转换成 vue-router 需要的格式 const transformRoute = (menus) => { return menus.map((menu) => { return { path: menu.path, name: menu.name, component: modules[menu.component], meta: { title: menu.title, }, }; }); }; // vue3需要手动实现该方法 const addRoutes = (routes) => { routes.forEach((route) => { router.addRoute(route); }); }; // 白名单页面,不需要权限 const whiteLists = ["/login", "/"]; router.beforeEach(async (to, from, next) => { // 是否是白名单 if (!whiteLists.includes(to.path)) { // 获取token const token = localStorage.getItem("token"); // 如果有token if (token) { // 获取用户菜单信息,从store里面获取 let userMenus = store.getters["getUserMenus"]; // 如果没有用户菜单信息就获取用户菜单信息 if (!userMenus) { userMenus = await store.dispatch("getUserMenuAction"); // 菜单转成路由 const userRoute = transformRoute(userMenus); // 动态添加路由 vue2 和 vue3 有细微差别 // vue2 // router.addRoutes(userRoute) // vue3 // 因为 vue3 移除了 router.addRoutes()方法,所以需要手动实现addRoutes方法。 addRoutes(userRoute); return next({ ...to }); } // 有name说明路由存在,否则说明没有该路由 if (to.name) { next(); } else { // 去无权限页面 next("/nopermission"); } } else { // 否则去登录页 next("/login"); } } else { // 是白名单则直接进入 next(); } }); // 修改标题的工作可以放在全局后置守卫 router.afterEach((to, from) => { if (to.meta.title) { document.title = to.meta.title; } });
我们再来看看 store 的逻辑
import { createStore } from "vuex"; export default createStore({ state: { userMenus: null, }, getters: { getUserMenus: (state) => state.userMenus, }, mutations: { setUserMenus(state, payload) { state.userMenus = payload; }, }, actions: { async getUserMenuAction({ commit }) { // 模拟后端获取用户菜单信息api const getUserMenuApi = () => { // 假设只有about菜单 return Promise.resolve([ { path: "/about", name: "About", component: "../views/About.vue", title: "关于", }, ]); }; const userMenus = await getUserMenuApi(); commit("setUserMenus", userMenus); return userMenus; }, }, });使用
import router from "./router"; // vue2写法 new Vue({ router, render: (h) => h(App), }).$mount("#app"); // vue3写法 const app = createApp(App); app.use(router).mount("#app");在没登录的情况下,去about和userlist页面会重定向到登录页。给本地添加token,模拟登录完成后,因为用户有about的菜单,所以去 about 页面是没问题的,去 userlist 页面则会重定向到没有权限页面。