vue3动态路由配置总结

1.动态路由使用背景

路由不在router文件中写死,而是在登录后通过后端获取路由数据,然后进行动态路由设置。

为什么要使用动态路由?可以进行权限管理,比如后端根据不同用户和超管会有返回不同的数据,这个数据控制路由、菜单、页面按钮等的显示和隐藏,从而进行权限控制。

2.主要有以下几点:

  1. 后端返回不同用户对应的路由数据
  2. 路由文件中只配置基本路由,如/ 和/login
  3. 登录后会返回路由数据,需要将数据保存到vuex中并且设置动态路由;
  4. 路由文件中也需要去设置动态路由;否则如果不是通过登录跳转的页面而是地址栏输入地址直接回车的就获取不到路由
  5. 通过路由守卫进行路由拦截

3.注意点:

  1. 嵌套路由的设置children(注意基础路由下一定要留children:[] 配置嵌套路由);
  2. vite项目中component组件的引入路径问题:不能使用@作为路径即使配置了alise别名;路径的目录级别问题;
  3. 基础路由和动态路由不能重名:重名后设置有问题;
  4. 动态路由设置时机:登录后以及路由文件中(也可以在main.js里面调用全局调用一次即可);
  5. 动态路由设置方法放在vuex中;
  6. 动态路由和路由守卫(token等)的配合;

4.具体实现

1.路由数据(Mock模拟)

import Mock from 'mockjs'
export default {
  getMenu: config => {
    const { username, password } = JSON.parse(config.body)
    // 先判断用户是否存在
    // 判断账号和密码是否对应
    if (username === 'admin' && password === 'pass') {
      return {
        code: 200,
        data: {
          menu: [
            {
              path: '/main',
              name: 'main',
              label: '首页',
              icon: 'house',
              url: 'home/Main'
            },
            {
              path: '/mall',
              name: 'mall',
              label: '商品管理',
              icon: 'video-play',
              url: 'mall/Mall'
            },
            {
              path: '/user',
              name: 'user',
              label: '用户管理',
              icon: 'user',
              url: 'user/User'
            },
            {
              label: '其他',
              icon: 'location',
              path:'/location',
              children: [
                {
                  path: '/page1',
                  name: 'page1',
                  label: '页面1',
                  icon: 'setting',
                  url: 'other/Page1'
                },
                {
                  path: '/page2',
                  name: 'page2',
                  label: '页面2',
                  icon: 'setting',
                  url: 'other/Page2'
                }
              ]
            }
          ],
          token: Mock.Random.guid(),
          message: '获取成功'
        }
      }
    } else if (username === 'lmf' && password === 'lmf') {
      return {
        code: 200,
        data: {
          menu: [
            {
              path: '/',
              name: 'main',
              label: '首页',
              icon: 's-home',
              url: 'home/Main'
            },
            {
              path: '/mall',
              name: 'mall',
              label: '商品管理',
              icon: 'video-play',
              url: 'mall/Mall'
            }
          ],
          token: Mock.Random.guid(),
          message: '获取成功'
        }
      }
    } else {
      return {
        code: -999,
        data: {
          message: '密码错误'
        }
      }
    }

  }
}

2. api配置

Mock.mock(/permission\/getMenu/, 'post', menuList.getMenu)

...
// 登录校验且获取菜单数据
export const getLoginMenu = async (data) =>{
    return await request({
        method:"post",
        url:'/permission/getMenu',
        data,
        // 这里true表示使用线上mock的数据,false表示使用本地api,如果本地有mock配置就用本地mock,没有返回404
        isMock: false
    });
}

3.基础路由配置

import { createRouter, createWebHashHistory } from "vue-router"
import store from '../store/index'
// 如果是vite不能使用别名,你需要加 “../”相当路由的方式。
const Main = () => import(/* webpackChunkName: "Main" */"../views/home/Main.vue")
const Home = () => import(/* webpackChunkName: "Home" */"../views/home/Home.vue")
const User = () => import(/* webpackChunkName: "User" */"../views/user/User.vue")
const Mall = () => import(/* webpackChunkName: "Goods" */"../views/mall/Mall.vue")
const Page1 = () => import(/* webpackChunkName: "Page1" */"../views/other/Page1.vue")
const Page2 = () => import(/* webpackChunkName: "Goods" */"../views/other/Page2.vue")
const Login = () => import(/* webpackChunkName: "Login" */"../views/Login.vue")

const routes = [{
  // 父级有name会警告[Vue Router warn]: The route named "root" has a child without a name and an empty path. Using that name won't render the empty path child so you probably want to move the name to the child instead. If this is intentional, add a name to the child route to remove the warning.
  name: 'home',
  path: "/",
  component: Home,
  redirect: '/main',
  children: []//一定要留children:[]用于设置嵌套路由
},
{
  path: "/login",
  component: Login,
}
];

const router = createRouter({
  history: createWebHashHistory(),
  routes
})
export default router;

4.登录后将数据保存到vuex中,并且设置动态路由

注意跳转到基础路由中

        let result = await getLoginMenu(formLogin);
        //   设置token
        store.commit("token/setToken", result.token);

        // 动态设置menu
        store.commit("layout/addMenuList", result.menu);
        //    动态添加路由
        store.commit("layout/setDynamicRoutes", router);

        router.push("/");

5.store中保存数据,且设置动态路由

这里一定要注意component的路径问题;

router.addRoute()的参数问题,是设置到需要嵌套的name为home的路由下

const state = {
    // 菜单栏
    menuList: JSON.parse(localStorage.getItem('layout') || '[]'),
}

const mutations = {

    addMenuList(state, payload) {
        state.menuList = payload;
    },
 if (!state.tags.includes(payload)) state.tags.push(payload);
    },
    delTags(state, payload) {
        // 删除时判断当前索引
        let index = state.tags.findIndex(item => item === payload);
        state.tags.splice(index, 1);
    },
    setDynamicRoutes(state, router) {
        // 循环menuList设置动态路由
        let menu = state.menuList;
        let menuArr = [];
        
        menu.length > 0 && menu.forEach(item => {
            // 注意这里对嵌套路由的处理,children只有重新赋值给children后再push到menuArr,层级才是对的
            if (item.children) {
                item.children = item.children.map(child => {
                    let url = `../../views/${child.url}.vue`;
                    const component = () => import(/* @vite-ignore */url);
                    child.component = component;
                    return child;
                });
                menuArr.push(...item.children);
            } else {
                // 注意这里路径问题(写成@不行,路径级别也必须对)
                let url = `../../views/${item.url}.vue`
                const component = () => import(/* @vite-ignore */url)
                item.component = component;
                menuArr.push(item);
            }
        });

        menuArr.forEach(item => {
            // 这里必须设置到对应的一级菜单下面
            router.addRoute("home", item);
        });
    }
}


export default {
    namespaced: true,
    state,
    mutations
}

6.动态路由和路由守卫及token的配合

主要是会调用一次store中全局设置路由的方法;

进行路由拦截的几种情况:这里很重要

// 路由守卫中再次设置路由(此时是在地址栏直接回车时处理)
store.commit("layout/setDynamicRoutes", router);

// 判断路由在配置的动态路由中是否存在,不存在则跳转到home,存在则直接跳转
const checkRouter = (path) =>{
  let hasCheck =  router.getRoutes().filter(item=>item.path == path).length;
  if (hasCheck) {
    return true
  } else {
    return false
  }
}

router.beforeEach((to, from, next) => {
  // 要重新获取token
  const token = store.state.token.token;
  
  //注意这里的逻辑: 如果咩有token且路由不为/login则直接跳转到login页面
  // 如果路由存在且有token则直接到home页面否则直接next
  if (!token && to.path !== '/login') {
    next('/login');
    // 否则检查路由是否存在,不存在直接跳转home页面
  }else if(!checkRouter(to.path)){
    next('/main');
  }else {
    // 存在则直接跳转
    next();
  }
});

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值