vue3实现动态菜单和动态路由和刷新后白屏处理

这篇具有很好参考价值的文章主要介绍了vue3实现动态菜单和动态路由和刷新后白屏处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:

项目中,当每一个角色得到的界面不一致的时候,我们就不能使用静态菜单了,而是要从后端得到动态的菜单数据,然后动态的将菜单数据展示在界面上。

除了在界面展示,也还要将界面的路由动态添加,在路由动态添加之后,你可能会出现刷新界面,界面变白的情况,页面刷新白屏其实是因为vuex引起的,由于刷新页面vuex数据会丢失,所以动态添加路由这一步也就失效了。这种情况我会在最后给一个处理方法。

所以这个博客会做两个部分的处理:

  1.         动态菜单生成
  2.         动态添加路由

vue动态路由导航菜单,vue.js,前端,javascriptvue动态路由导航菜单,vue.js,前端,javascript

 

 

动态菜单生成

1.获得后端数据(有mock模拟数据,也可以使用后端接口)

1.1使用mock得到模拟数据

没有下载mock的可以查看:Vue项目中使用mockjs实现mock模拟数据 - ykCoder - 博客园 (cnblogs.com)

mock/modules/menu.js  保存模拟的后端数据

function list(res) {
  // res是一个请求对象,包含: url, type, body
  return {
    code: 200,
    message: "请求成功",
    //菜单数据,可以修改成你自己要的菜单
    data: [
      {
        id: "600d4075e218daaf4ec77e50",
        menuType: "1",
        menuName: "首页",
        path: "/Home",
        icon: "house",
      },
      {
        id: "600d4075e218daaf4ec77e51",
        menuType: "1",
        menuName: "公司管理",
        path: "/company",
        icon: "location",
        children: [
          {
            id: "600d525e602f452aaeeffcd9",
            menuType: "1",
            menuName: "公司资料",
            path: "/company/Company",
          },
          {
            id: "601bc4f8a794e23c2e42efa9",
            menuType: "1",
            menuName: "个人资料",
            path: "/company/Person",
          },
        ],
      },
  };
}

//暴露list
export default { list };

mock/index.js  引入mock/menu.js

// 引入mockjs
import Mock from 'mockjs'
// 引入模板函数类
import menu from './modules/menu'

// Mock函数
const { mock } = Mock

// 设置延时
Mock.setup({
  timeout: 400
})

// 使用拦截规则拦截命中的请求,mock(url, post/get, 返回的数据);
Mock.mock('/mock/menu', 'get', menu.list)

在界面引用

<script>
export default {
  data() {
    return {
      menuData:[],
    };
  },
  methods: {
    getMenu() {
      this.$http.get('/mock/menu').then((res) => {
        console.log(res)
        if (res.data.code === 200) {
          this.menuData = res.data.data;
          // console.log(this.menuData,"menuData")
          //获取菜单的数据,存入store中
          this.$store.commit("setMenu",this.menuData)
          //动态生成路由
          this.$store.commit("addMenu",this.$router)
        }
      })
    },
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
  },
};                   
</script>

1.2连接后端接口(统一接口管理),从后端得到菜单数据

如果对统一接口管理,有不明白的可以查看:

016-尚硅谷-尚品汇-API接口统一管理_哔哩哔哩_bilibili​​​​​​

Vue封装接口思路(包括请求(响应拦截器))_vue接口封装_忧郁火龙果的博客-CSDN博客

api/request.js

import axios from 'axios';

//1.利用axios对象的方法create,去创建一个axios实例。
const requests = axios.create({
    //配置对象
    //接口当中:路径都带有/api     基础路径,发送请求的时候,路径当中会出现api
    baseURL:"/api",
    //代表请求超时的时间
    timeout:5000,
})
//请求拦截器:
requests.interceptors.request.use((config) =>{
    //config:配置对象,对象里面有一个属性很重要,header请求头
    return config;
})

//响应拦截器
requests.interceptors.response.use((res)=>{
    //成功的回调函数:服务器相应数据回来以后,响应拦截器可以检测,可以做一些事情
    return res.data;
},(error)=>{
    //失败的回调函数
    return Promise.reject(new Error('faile'));
})

//对外暴露
export default requests;

api/menu.js

import requests from "./request";

export const menuList = (data) => {
  return requests({
    url: "/user/menus",
    method: 'GET',
    data: data,
  });
};

在界面引用

<script>
import { menuList } from "@/api/menu.js";

export default {
  data() {
    return {
      menuData:[],
    };
  },
  methods: {
    getMenu() {
      const id = 1;
      //假数据
      const res = menuList(id);
      console.log(res.data);
      if (res.code == 200) {//获得菜单导航数据
      this.menuData = res;
      } else {//没有获得菜单数据
      }
    },
  },
};                   
</script>

2.接收界面数据,实现动态界面

2.1界面实现

我的菜单界面是用两个vue文件写的。组件间的传值要用vuex,这里建议去看一下官网学习一下。

组件间的传值:Vue组件之间的传值 - 掘金 (juejin.cn)

HomeMenu.vue 

这里实现二级菜单用的是递归的方法,这个是我觉得很神奇的地方,我第一次在vue中使用到了递归。在此之前我觉得vue就只能实现做界面的功能,没有想到过vue也可以这么灵活。

<home-menu :menuData="item.children" />//在组件中调用组件本身,使用递归的方法实现二级目录。

全部代码 

<template>
  <div>
    <template v-for="item in menuData" :key="item">
      <el-sub-menu
        :index="item.id"
        v-if="
          item.children &&
          item.children.length > 0 &&
          item.children[0].menuType.toString() === '1'
        "
      >
        <template #title>
          <el-icon><component :is="item.icon" /></el-icon>
          <span>{{ item.menuName }}</span>
        </template>
        <home-menu :menuData="item.children" />
      </el-sub-menu>
      <el-menu-item
        @click="clickMenu(item)"
        v-else-if="item.menuType.toString() === '1'"
        :index="item.path"
        :key="item.id"
      >
        <template #title>
          <el-icon><component :is="item.icon" /></el-icon>
          <span>{{ item.menuName }}</span>
        </template>
      </el-menu-item>
    </template>
  </div>
</template>
<script>
export default {
  name: "home-menu",
  //为了实现组件间的传值
  props: ["menuData"],
  methods: {
    //点击菜单
    clickMenu(item) {
      console.log("item:" + item);
      //当前路由与跳转路由不一致时跳转
      if (this.$route.path !== item.path && !(this.$route.path === '/home' && (item.path === '/'))) {
        this.$router.push(item.path);
      }
    },
  },
};
</script>

HomeAside.vue

<template>
  <div>
    <el-menu
      active-text-color="#ffd04b"
      background-color="#545c64"
      class="el-menu-vertical-demo"
      default-active="2"
      text-color="#fff"
      @open="handleOpen"
      @close="handleClose"
    >
      <h3>{{ isCollapse ? "排班" : "智能排班系统" }}</h3>
      <home-menu :menuData="menuData"></home-menu>
    </el-menu>
  </div>
</template>
<script>
import HomeMenu from "@/components/menu/HomeMenu.vue";
import { thisTypeAnnotation } from "@babel/types";
import { mapState } from 'vuex';

export default {
  components: {
    "home-menu": HomeMenu,
  },
  data() {
    return {
      menuData:[],
    };
  },
  mounted() {
    //获得菜单
    this.getMenu();
  },
  computed: {
    //给store传递menuData的值
    ...mapState({
      menuData: (state) => state.menu.menuData
    }),
  },
  methods: {
    getMenu() {
      this.$http.get('/mock/menu').then((res) => {
        console.log(res)
        if (res.data.code === 200) {
          this.menuData = res.data.data;
          // console.log(this.menuData,"menuData")
          //获取菜单的数据,存入store中
          this.$store.commit("setMenu",this.menuData)
          //动态生成路由
          this.$store.commit("addMenu",this.$router)
        }
      })
    },
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
  },
};                   
</script>

<style lang="less" scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}
.el-menu {
  height: 100vh;
  border-right: none;
  h3 {
    color: #fff;
    text-align: center;
    line-height: 48px;
    font-size: 16px;
    font-weight: 400px;
  }
}
</style>

2.2组价间传值的使用

store/menu.js  这里的addMenu函数值实现动态路由的关键,在下面会有分析

export default {
  state: {
    // 动态菜单
    menuData: [],
  },
  //修改字段
  mutations: {
    //设置菜单的数据
    setMenu(state, val) {
      state.menuData = val;
    },
    //动态注册路由
    addMenu(state, router) {
      // 处理动态路由的数据
      const menuData = JSON.parse(JSON.stringify(state.menuData));
      const menuArray = [];
      menuData.forEach((item) => {
        if (item.children && item.children.length >= 1) {
          menuArray.push(...item.children);
        } else {
            menuArray.push(item);
        }
      });
      console.log(menuArray, "menuArray");
      // 路由的动态添加
      if (menuArray[0] !== "") {
        menuArray.forEach((item) => {
          router.addRoute("main", { path: `${item.path}`,component: () => import(`@/views${item.path}.vue`) });
        });
      }
    },
  },
};

menu/index.js

import { createStore } from 'vuex'
import createPersistedState from "vuex-persistedstate"
import menu from './menu'


export default createStore({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    menu
  },
  /* vuex数据持久化配置 */
  plugins: [
    createPersistedState({
      // 存储方式:localStorage、sessionStorage、cookies
      storage: window.sessionStorage,
      // 存储的 key 的key值
      key: "store",
      reducer(state) { //render错误修改
        // 要存储的数据:本项目采用es6扩展运算符的方式存储了state中所有的数据
        return { ...state };
      }
    })
  ]
})

3.实现动态路由

3.1实现动态路由的代码分析

上面的代码已经实现了动态路由,这里是解释一下动态路由的关键上面,网上关于动态路由的代码很多,但是对于第一次做动态路由的人来说,想要去看懂事有点难度的。

创建一个新的空数组,遍历menuData的时候根据元素有没有children来分别处理,将需要的数据保存到新数组中,通过传入的path路径来添加路由。

最重要的部分来了,vue router4的版本不再使用router.addRoutes而是router.addRoute,这个地方建议看官方文档

动态路由 | Vue Router (vuejs.org)

 router.addRoute("main", { path: `${item.path}`,component: () => import(`@/views${item.path}.vue`) });
//动态注册路由
    addMenu(state, router) {
      // 处理动态路由的数据
      const menuData = JSON.parse(JSON.stringify(state.menuData));
      const menuArray = [];
      menuData.forEach((item) => {
        if (item.children && item.children.length >= 1) {
          menuArray.push(...item.children);
        } else {
            menuArray.push(item);
        }
      });
      console.log(menuArray, "menuArray");
      // 路由的动态添加
      if (menuArray[0] !== "") {
        menuArray.forEach((item) => {
          router.addRoute("main", { path: `${item.path}`,component: () => import(`@/views${item.path}.vue`) });
        });
      }
    },

下面贴一下vue2项目的写法,这里我使用时先把元素中添加component属性,这里和上面的写法有点不一样,但是我建议还是用上面的好一点,这个写法可能会出bug。

item.component =  (resolve) => require([`@/views/home/${item.url}`], resolve)
//动态注册路由
        addMenu(state, router) {
            // 处理动态路由的数据
            const menuArray = []
            state.menuData.forEach(item => {
                if (item.children) {
                    item.children = item.children.map(item => {
                        item.component = (resolve) => require([`@/views/home/${item.url}`], resolve)
                        return item
                    })
                    menuArray.push(...item.children)
                } else {
                    item.component =  (resolve) => require([`@/views/home/${item.url}`], resolve)
                    menuArray.push(item)
                }
            })
            console.log(menuArray, 'menuArray')
            // 路由的动态添加
            menuArray.forEach(item => {
                router.addRoute('main', item)
            })
        },

还有一个处理方法,你可以在传过来的数据中就传path,component的值,最后直接使用

router.addRoute() 调用就行了。

在HomeAside.vue界面之间的引用

//获取菜单的数据,存入store中
this.$store.commit("setMenu",this.menuData)
//动态生成路由
this.$store.commit("addMenu",this.$router)

3.2刷新界面后,白屏处理

恭喜你,来到最后一步,在最前面的时候说过,白屏问题是因为vuex引起的,由于刷新页面vuex数据会丢失,所以动态添加路由这一步也就失效了。我在网上找了很多方法都没有解决,最后回归到问题的本质,刷新界面vuex的数据会丢失,那么我们不让数据丢失不就行了,我的处理方法是在

mian.ts中再保存一遍路由的数据。

这个是vue3的处理:

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import * as ElIconModules from "@element-plus/icons";

import '@/mock';
import axios from 'axios';
import VueAxios from 'vue-axios';

//动态菜单路由的生成
const addMenu = () => {
  store.commit("addMenu",router)
}
addMenu()

const app = createApp(App);
app.use(store).use(router).use(ElementPlus).use(VueAxios,axios).mount("#app");

// 统一注册Icon图标
for (const iconName in ElIconModules) {
  if (Reflect.has(ElIconModules, iconName)) {
    const item = ElIconModules[iconName];
    app.component(iconName, item);
  }
}

这个是vue2的处理:

import Vue from 'vue';
import router from './router'
import store from './store'
import App from './App.vue'


import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI);

import axios from 'axios'
//配置请求根路径
axios.defaults.baseURL = "http://localhost:8088"                                 
//将axios作为全局的自定义属性,每个组件可以在内部组件访问
Vue.prototype.$http = axios

//添加全局前置导航守卫
router.beforeEach((to, from, next) => {
  //判断token是否存在
  // localStorage.setItem("token", message.data.token);
  const token = localStorage.getItem("token");
  // localStorage.clear();
  console.log(token,'token')
  if( !token && to.name !== 'login' ){//token不存在,没有登录
      next({ name : 'login' })
  } else {
    next();
  }
})

new Vue({
  router,
  store,
  el: '#app',
  created() {
    store.commit('addMenu',router)
  },
  render: h => h(App)
});

好了,最后希望大家都能看懂,如果有什么问题可以提出来。文章来源地址https://www.toymoban.com/news/detail-737906.html

到了这里,关于vue3实现动态菜单和动态路由和刷新后白屏处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue2+antd——实现动态菜单路由功能——基础积累

    最近在写后台管理系统,遇到一个需求就是要将之前的静态路由改为动态路由,使用的后台框架是: vue-antd-admin 然后通过 loadRoutes 方法来实现异步动态路由。 如上图所示,需要在登录接口调用成功后,书写以下的代码: import { loadRoutes } from \\\'@/utils/routerUtil.js\\\'; import { getCodeL

    2024年02月08日
    浏览(29)
  • (二) Vue3 + Element-Plus 实现动态菜单栏

    系列介绍:Vue3 + Vite + TS 从零开始学习 项目搭建:(一) Vue3 + Vite + TS 项目搭建 实现动态菜单栏:(二) Vue3 + Element-Plus 实现动态菜单栏 实现动态面包屑:(三) Vue3 + Element-Plus 实现动态面包屑 实现动态标签页:(四) Vue3 + Element-Plus 实现动态标签页 实现动态主题色切换(demo):(五)

    2023年04月23日
    浏览(42)
  • vue3.0+element Plus实现页面布局,侧边栏菜单路由跳转

    一. 先在router.js文件中配置路由,将侧边栏中需要跳转的页面都添加到children中 二. 在view目录下新建一个文件,里面包含侧边栏要跳转的页面 三.  页面样式布局 1. 我选择使用自定义组件BaseLayout.vue文件来设置header和aside样式显示 也可以使用element plus中的Container 布局容器 

    2024年02月13日
    浏览(36)
  • Vue3 + Element Plus 实现动态标签页及右键菜单

    目录 先上图  使用el-dropdown绑定右键菜单,为每个tab页绑定一个右键 右键菜单生效后控制每个下拉项的禁用与显示(每一项代表一个功能) 每个右键项对应的功能  控制每次只显示一个右键 完整代码         只有首页的情况         多个tab页的情况  

    2024年02月07日
    浏览(31)
  • vue3后台管理系统实现动态侧边导航菜单管理(ElementPlus组件)

    记住 一级(el-sub-menu)的都是只是展示的 点击跳转的都是一级下的子级(el-menu-item) 完整展示 1:在登陆功能进行登陆 获取menu列表 注册路由表的时候 把文件进行创建好 因为注册的方法需要获取这个路径 整个router下的main product等等都要创建 2:侧边菜单界面 router/index.ts

    2024年02月16日
    浏览(38)
  • vue3 + TS + elementplus + pinia实现后台管理系统左侧菜单联动实现 tab根据路由切换联动内容

    效果图:  home.vue页面代码 left.vue页面代码 tab.vue页面代码 pinia里面的代码 安装 使用插件  在main.ts中注册 路由代码 我把代码放git上了,有需要的自行拉取 https://gitee.com/Flechazo7/vue3.git

    2024年02月09日
    浏览(30)
  • Vue-根据角色获取菜单动态添加路由

    如果大家写过后台管理系统的项目,那么动态路由一定是绕不开的,如果想偷懒的话,就把所有路由一开始都配置好,然后只根据后端返回的菜单列表渲染就好菜单就好了,但是这样的隐患就是我在地址栏输入的地址的时候,也会进入这个页面,不偷懒的方法就是本文要介绍

    2024年01月24日
    浏览(65)
  • vue3 element-plus动态菜单及动态图标

    引入element-plus 注册图标组件 动态引入图标代码 完整代码 路由如下

    2024年01月18日
    浏览(31)
  • vue 动态路由刷新失效及404页面处理

    在开发后台管理项目,我们会使用vue动态路由做权限管理,但是使用vue动态路由时会遇到一些坑,这里总结一下,并提供解决思路 问题:刷新页面时会把addRouter添加的动态路由刷新掉,因此浏览器找不到之前添加的路由,便会进入白屏页面或者404页面 处理方式:判断是否刷

    2024年02月12日
    浏览(44)
  • 前端之vue 根据菜单自动生成路由(动态配置前端路由)

    在需要权限控制的页面,往往存在根据用户来显示菜单的情况,单独根据用户类型判断显然不是很好,如果后面用户类型发生变化,项目修改维护可能就会比较麻烦,所以比较好的做法是根据后端返回的菜单动态生成页面路由,以达到完全权限控制的目的,并且若权限发生变

    2024年04月10日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包