vue+ivewui实现动态路由添加,并解决刷新页面空白和页面死循环

困扰了好几天的动态路由问题终于解决了,参考过很多篇文章,在此感谢各位博主的分享。
现将个人心得整理如下,希望能给大家带来更多的帮助。

一.动态路由的添加

1.准备工作

//登录接口返回数据格式

{
	routes:[],
	token:'',
	userInfo:{}
}

//静态路由(routes.js)


/**
 * 在主框架内显示
 */

const frameIn = [
    {
        path: '/',
        redirect: {
            name: 'dashboard-console'
        },
        component: BasicLayout,
        children: [
            {
                path: 'index',
                name: 'index',
                redirect: {
                    name: 'dashboard-console'
                }
            },
            {
                path: 'log',
                name: 'log',
                meta: {
                    title: '前端日志',
                    auth: true
                },
                component: () => import('@/pages/system/log')
            },
            // 刷新页面 必须保留
            {
                path: 'refresh',
                name: 'refresh',
                hidden: true,
                component: {
                    beforeRouteEnter (to, from, next) {
                        next(instance => instance.$router.replace(from.fullPath));
                    },
                    render: h => h()
                }
            },
            // 页面重定向 必须保留
            {
                path: 'redirect/:route*',
                name: 'redirect',
                hidden: true,
                component: {
                    beforeRouteEnter (to, from, next) {
                        next(instance => instance.$router.replace(JSON.parse(from.params.route)));
                    },
                    render: h => h()
                }
            }
        ]
    },
    dashboard
];

/**
 * 在主框架之外显示
 */

const frameOut = [
    // 登录
    {
        path: '/login',
        name: 'login',
        meta: {
            title: '$t:page.login.title'
        },
        component: () => import('@/pages/account/login')
    }
];

/**
 * 错误页面
 */
const errorPage = [
    {
        path: '/403',
        name: '403',
        meta: {
            title: '403'
        },
        component: () => import('@/pages/system/error/403')
    },
    {
        path: '/500',
        name: '500',
        meta: {
            title: '500'
        },
        component: () => import('@/pages/system/error/500')
    }
];

// 导出需要显示菜单
export const frameInRoutes = frameIn;
window.frameInRoutes = frameInRoutes;
//*404页面需要单独拿出来,在添加完动态路由之后添加,不然刷新页面会报404,跳转到404页面*
export const page404 = {
    path: '*',
    name: '404',
    meta: {
        title: '404'
    },
    component: () => import('@/pages/system/error/404')
};
// 重新组织后导出
export default [
    ...frameIn,
    ...frameOut,
    ...errorPage
];

// 获取和组装动态路由api,route-util.js

/**
 * 动态添加路由,将菜单组装为路由格式
 */
import mg from "@/libs/core/mg";
import BasicLayout from '@/layouts/basic-layout';

const routeUtil = {
    /**
     * 动态组装路由
     */
    getAsyncRoute: function(menus) {
        let newDynamicRoute;
        if (menus) {
            newDynamicRoute = this.generateRoute(menus);
            return newDynamicRoute;
        }
        // 向后端请求路由数据
        mg.action.getData(mg.path.login.getRouters,{}).then(res => {
            newDynamicRoute = this.generateRoute(res.data);
            return newDynamicRoute;
        });
    },
    /**
     * 拼装路由
     */
    generateRoute: function(menus){
        // 动态加载路由,routes 为数组
        let routes = backendMenusToRouters(menus);
       return routes;
    }
}


/**
 * @description 将后端菜单树转换为路由树
 * @param {Array} menus
 * @returns {Array}
 */
export const backendMenusToRouters = (menus) => {
    let routers = [];
    menus.forEach(menu => {
        // 将后端数据转换成路由数据
        let route = backendMenuToRoute(menu);
        // 如果后端数据有下级,则递归处理下级
        if (menu.children && menu.children.length !== 0) {
            route.redirect = {
                name: menu.children[0].name
            };
            route.children = backendMenusToRouters(menu.children);
        }
        routers.push(route);
    });
    return routers;
}

/**
 * @description 将后端菜单转换为路由
 * @param {Object} menu
 * @returns {Object}
 */
const backendMenuToRoute = (menu) => {
    // 原先routers写法是component: () => import('@/view/error-page/404.vue')
    let route = Object.assign({}, menu);
    if(route.component){
        if (route.component === 'BasicLayout') {
            route.component = BasicLayout;
        } else {
            route.component = () => import(`@/${menu.component}.vue`);
        }
    }
    route.meta = {
        title: menu.title,
        auth: menu.auth
    };
    return route;
}

export default routeUtil;

2.动态添加路由

在登录接口返回结果中,组装了动态路由相关信息。动态路由的添加时机是在登录后,页面跳转前,不然访问菜单会出现空白或404

// 登录相关api—与后台交互接口accout.js,代码量比较大,这里只整理出登录按钮执行的方法

/**
 * 登录
 * */
import util from '@/libs/util';
import router from '@/router';
import Setting from '@/setting';
import { AccountLogin, AccountRegister, AccountLogout } from '@/api/account';

import { Modal } from 'view-design';

export default {
    namespaced: true,
    actions: {
        /**
         * @description 登录
         * @param {Object} param context
         * @param {Object} param username {String} 用户账号
         * @param {Object} param password {String} 密码
         * @param {Object} param route {Object} 登录成功后定向的路由对象 任何 vue-router 支持的格式
         */
        login ({ dispatch }, {
            username = '',
            password = ''
        } = {}) {
            return new Promise((resolve, reject) => {
                // 开始请求登录接口
                AccountLogin({
                    username,
                    password
                })
                    .then(async res => {
                        let userInfo  = res.userInfo;
                        util.cookies.set('uuid', userInfo.userId);
                        util.cookies.set('token', res.token);
                        // // 设置 vuex 用户信息
                        await dispatch('admin/user/set', userInfo, { root: true });
                        window.localStorage.setItem("roles",JSON.stringify(userInfo.roles));
                        window.localStorage.setItem("loginUser",JSON.stringify(userInfo));
                        window.localStorage.setItem("menuSider",JSON.stringify(res.routers));
                        window.localStorage.setItem("asyncMenus",JSON.stringify(res.routers));
                        // 用户登录后从持久化数据加载一系列的设置
                        await dispatch('load', { loadOpenedTabs: Setting.page.loadOpenedTabs });
                        // 结束
                        resolve();
                    })
                    .catch(err => {
                        console.log('err: ', err);
                        reject(err);
                    })
            })
        }

// 登录相关api----登录按钮事件方法

/**
 * @description 登录
 * 表单校验已有 iView Pro 自动完成,如有需要修改,请阅读 iView Pro 文档
*/
handleSubmit (valid, values) {
    if (valid) {
        const { username, password } = values;
        this.login({
            username,
            password
        }).then(() => {
            //动态添加路由
            let menus = JSON.parse(localStorage.getItem('asyncMenus'));
            let asyncRoutes = routeUtil.getAsyncRoute(menus);
            this.$router.addRoutes(asyncRoutes);
            this.$router.addRoute(page404);
            window.frameInRoutes = frameInRoutes.concat(asyncRoutes);
            this.$store.commit('admin/menu/setAsyncMenu', asyncRoutes);
            // 重定向对象不存在则返回顶层路径
            this.$router.replace(this.$route.query.redirect || '/').catch(err => err);
        }).catch(err => {
            console.log('err: ', err);
            this.loginAlert.show = true;
        });
    }
}

至此,第一步添加动态路由的过程就完成了。
但是刷新页面后,页面空白,因为每次刷新后,路由被重置,动态路需要再次添加,添加位置一般在路由守卫,即router.beroreEach中添加,如下代码是router/index.js的全部内容

import Vue from 'vue';
import VueRouter from 'vue-router';
import iView from 'view-design';

import util from '@/libs/util'

import Setting from '@/setting';

import store from '@/store/index';

// 路由数据
import routes, {page404} from './routes';
import routeUtil from "@/router/route-util";
import mg from "@/libs/core/mg";

Vue.use(VueRouter);

// 导出路由 在 main.js 里使用
const router = new VueRouter({
    routes,
    mode: Setting.routerMode,
    base: Setting.routerBase
});

/**
 * 路由拦截
 * 权限验证
 */
router.beforeEach((to, from, next) => {
    if (Setting.showProgressBar) iView.LoadingBar.start();
    let token = util.cookies.get('token');
    let asyncMenu = JSON.stringify(store.state.admin.menu.asyncMenu);
    if (token) {
        // 登录过就不能访问登录界面,需要中断这一次路由守卫,执行下一次路由守卫,并且下一次守卫的to是主页
        if (to.path === '/login') {
            next({ path: '/' });
        }
        // 保存在store中路由不为空则放行 (如果执行了刷新操作,则 store 里的路由为空,此时需要重新添加路由)
        if ('[]' !== asyncMenu || to.name != null) {
            //放行
            next();
        } else {
            router.dynamicAddRouter(to,from,next);
        }
    } else {
        // 未登录时,注意 :在这里也许你的项目不只有 logon 不需要登录 ,register 等其他不需要登录的页面也需要处理
        if (to.path !== '/login') {
            next({ path: '/login' });
        } else {
            next();
        }
    }
});

router.afterEach(to => {
    if (Setting.showProgressBar) iView.LoadingBar.finish();
    // 多页控制 打开新的页面
    if (!('meta' in to) || (to.meta && !('tabs' in to.meta)) || (to.meta && to.meta.tabs)) {
        store.dispatch('admin/page/open', to);
    }
    // 更改标题
    util.title({
        title: to.meta.title
    });
    // 返回页面顶端
    window.scrollTo(0, 0);
});

/**
 * 添加动态路由
 * @param to 下一个路由
 * @param from 当前路由
 * @param next 放行
 */
router.dynamicAddRouter = function(to,from,next){
    // 将路由添加到 store 中,用来标记已添加动态路由
    mg.action.getData(mg.path.login.getRouters,{}).then(res => {
        let asyncRoutes = routeUtil.generateRoute(res.data);
        store.commit('admin/menu/setAsyncMenu', asyncRoutes);
        window.frameInRoutes = frameInRoutes.concat(asyncRoutes).concat(page404);
        router.$addRoutes(asyncRoutes);
        router.$addRoute(page404);
        // 如果 addRoutes 并未完成,路由守卫会一层一层的执行执行,直到 addRoutes 完成,找到对应的路由
        next({ ...to, replace: true });
    });
}

/**
 * 解决问题:动态路由添加时调用addRoutes();它只会帮你注入路由,不会帮你把前面的路由清掉。如此一来就重复添加了。
 * @param params
 */
router.$addRoutes = params => {
    params.forEach(item => {
        router.addRoute(item);
        router.options.routes.push(item);
    });
};
router.$addRoute = param => {
    router.addRoute(param);
    router.options.routes.push(param);
};

export default router;

通过此方法,可以解决刷新页面空白和router死循环问题。
关于动态路由刷新页面空白和死循环原因,这篇博客讲的比较明了,参考地址如下:https://blog.csdn.net/weixin_42094764/article/details/116403423

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值