Vue Router搭配Vuex动态路由
1. 定义基础路由:定义应用的基础路由,通常包含静态页面如登录页、404 页等。
2. 异步获取路由数据:从后台 API 获取用户或者角色对应的路由数据。
3. 动态添加路由:根据获取的路由数据搭配Vuex动态生成 Vue Router 实例,并添加到当前路由配置中。
2. 异步获取路由数据:从后台 API 获取用户或者角色对应的路由数据。
3. 动态添加路由:根据获取的路由数据搭配Vuex动态生成 Vue Router 实例,并添加到当前路由配置中。
1. 定义基础路由
基础路由通常是一些不变的页面,比如登录页、404 错误页等。
// src/router/staticRoutes.js
import Layout from '@/layout/index.vue';
export const staticRoutes = [
{
path: '/login',
component: () => import('@/views/login/index.vue'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404.vue'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index.vue'),
meta: { title: 'Dashboard', icon: 'dashboard' }
}]
}
];
2. 异步获取路由数据
通过 API 获取用户或角色对应的路由数据。
// src/api/user.js
import axios from 'axios';
export function getRoleMenuList(roleId) {
return axios.get(`/api/role/${roleId}/menu`);
}
3. 动态添加路由
根据后端返回的数据动态生成路由并添加到路由实例中。
3.1 动态路由生成函数
把后端的菜单数据转为 Vue Router 格式。
// src/utils/asyncRoutes.js
import Layout from '@/layout/index.vue';
const _import = require('@/router/_import_' + process.env.NODE_ENV);
export function parseAsyncRoutes(routes) {
const res = [];
routes.forEach(route => {
const tmp = { ...route };
if (tmp.component) {
if (tmp.component === 'Layout') {
tmp.component = Layout;
} else {
tmp.component = _import(tmp.component);
}
}
if (tmp.children) {
tmp.children = parseAsyncRoutes(tmp.children);
}
res.push(tmp);
});
return res;
}
3.2 动态添加路由
在 Vuex 中通过 action 动态获取和添加路由。(permission模块)
// src/store/modules/permission.js
import { getRoleMenuList } from '@/api/user';
import { parseAsyncRoutes } from '@/utils/asyncRoutes';
import { staticRoutes } from '@/router/staticRoutes';
const state = {
routes: [],
addRoutes: []
};
const mutations = {
SET_ROUTES: (state, routes) => {
// 清空之前的动态路由
state.addRoutes.forEach(route => {
if (router.hasRoute(route.name)) {
router.removeRoute(route.name);
}
});
state.addRoutes = routes;
state.routes = staticRoutes.concat(routes);
// 添加新路由
routes.forEach(route => {
router.addRoute(route);
});
}
};
const actions = {
async generateRoutes({ commit }, roleId) {
try {
const response = await getRoleMenuList(roleId);
const { data } = response;
const asyncRoutes = parseAsyncRoutes(data);
commit('SET_ROUTES', asyncRoutes);
return asyncRoutes;
} catch (error) {
throw new Error(`Failed to generate routes: ${error.message}`);
}
}
};
export default {
namespaced: true,
state,
mutations,
actions
};
在退出or登录时重置路由信息。(user模块)
// src/store/modules/user.js
import router, { resetRouter } from '@/router'; // 确保引入路由实例
import { login, getRoleMenuList } from '@/api/user'; // 确保引入login、getRoleMenuList 方法
import { parseAsyncRoutes } from '@/utils/asyncRoutes';
import { removeToken, setToken } from '@/utils/auth'; // 假设有 token 管理工具
const getDefaultState = () => {
return {
token: '',
name: '',
roles: []
};
};
const state = getDefaultState();
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState());
},
SET_TOKEN: (state, token) => {
state.token = token;
},
SET_NAME: (state, name) => {
state.name = name;
},
SET_ROLES: (state, roles) => {
state.roles = roles;
}
};
const actions = {
async login({ commit, dispatch }, userInfo) {
const { username, password } = userInfo;
try {
// 登录请求
const response = await login({ username, password });
const { token, roleId } = response.data;
commit('SET_TOKEN', token);
setToken(token); // 保存 token
// 添加新的动态路由
await dispatch('permission/generateRoutes', roleId, { root: true });
// 重置路由
resetRouter();
} catch (error) {
throw new Error('Login failed');
}
},
logout({ commit }) {
return new Promise((resolve) => {
removeToken(); // 移除 token
resetRouter(); // 重置路由
commit('RESET_STATE');
resolve();
});
}
};
export default {
namespaced: true,
state,
mutations,
actions
};
4. 在 Vue 应用中使用
在 Vuex 的主模块整合 permission 和 user 子模块
// src/store/index.js
import { createStore } from 'vuex';
import permission from './modules/permission';
import user from './modules/user';
const state = {
// 全局 state
};
const mutations = {
// 全局 mutations
};
const actions = {
// 全局 actions
};
// 创建 Vuex store 实例
const store = createStore({
state,
mutations,
actions,
modules: {
permission,
user
}
});
export default store;
在 router 主文件中引入这些配置,并在创建 Vue 实例前加载动态路由。
// src/router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import { staticRoutes } from './staticRoutes';
Vue.use(Router);
const createRouter = () => new Router({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: staticRoutes
});
const router = createRouter();
export function resetRouter() {
const newRouter = createRouter();
router.matcher = newRouter.matcher; // reset router
}
export default router;
在 main.js 中处理动态路由加载。
// src/main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store';
import router from './router';
import { getToken, getCurrentUserRoleId } from '@/utils/auth'; // 获取 token 和角色 ID 的工具函数
import { resetRouter } from '@/router';
//当 Vue.config.productionTip 设置为 false 时,Vue 在启动时不会在控制台输出这条提示信息。
//这对生产环境是有意义的,因为在生产环境中,你不需要这些开发相关的提示信息。
//Vue.config.productionTip = false;
async function initializeApplication() {
try {
const token = getToken();
if (token) {
const roleId = getCurrentUserRoleId();
if (roleId) {
await store.dispatch('permission/generateRoutes', roleId);
router.addRoutes(store.state.permission.addRoutes);
}
}
} catch (error) {
console.error('Failed to initialize application:', error);
} finally {
// 无论是否成功初始化,都要创建 Vue 实例
createVueInstance();
}
}
function createVueInstance() {
new Vue({
el: '#app',
router,
store,
render: h => h(App)
});
}
initializeApplication();
本文来自博客园,作者:苏沐~,转载请注明原文链接:https://www.cnblogs.com/sumu80/p/18247899