基于vue-router的从后端动态加载菜单的实现

  • 源码下载
  • 前言
  • 后端模拟加载菜单的例子实现
    • VueRouterController.java
    • CorsConfig.java
    • application.yml
    • 验证结果
  • 前端例子的实现
    • 编写axios工具
    • 编写API接口
    • 编写测试页面
      • 1.vue
      • 2.vue
      • 3.vue
    • 改造App.vue实现动态加载路由
    • 验证动态路由

源码下载


大家可以直接微信扫描上面的二维码关注我的公众号,然后回复20200113 里面就会给到源代码的下载地址同时会附上相应的视频教程,并定期的与大家分享相关的技术文章。

前言

在前后端分离架构的开发过程中,我们有两种方式来管理的我们的路由,最常见的一种方式就是直接将配置写在前端的代码中,这种方式写起来十分的方便,但是当我们需要做到一些动态菜单的时候就没办法实现了【比如动态的配置相关的报表页面,也许有人会说我们的报表每个都是定制化开发的,那你就用第一种实现方式吧】。,这时候我们就需要用到我们的另外一种的路由实现方式,就是从后端直接读取当前用户的路由信息进行前端的动态加载。

后端模拟加载菜单的例子实现

后端我们直接使用intellij 创建一个spring-boot工程,非常的简单就一个【VueRouterController.java】路由请求的controller、【CorsConfig.java】放开跨域请求的配置以及最后一个application.yml的配置。

VueRouterController.java

package com.vue.router.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author linzf* @since 2020/1/13* 类描述:*/
@RestController
@RequestMapping("menu")
public class VueRouterController {/*** 功能描述: 加载菜单* @return*/@PostMapping("loadMenu")public Map<String,Object> loadTree(){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}Map<String,Object> result = new HashMap<>(3);result.put("code",200);result.put("msg","菜单加载成功!");List<Map<String,Object>> obj = new ArrayList<>();Map<String,Object> tree = new HashMap<>(3);tree.put("routerPath","views/1.vue");tree.put("name","hello1");tree.put("path","/hello1");obj.add(tree);tree = new HashMap<>(3);tree.put("routerPath","views/2.vue");tree.put("name","hello2");tree.put("path","/hello2");obj.add(tree);tree = new HashMap<>(3);tree.put("routerPath","views/3.vue");tree.put("name","hello3");tree.put("path","/hello3");obj.add(tree);result.put("obj",obj);return result;}}

CorsConfig.java

package com.vue.router.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author linzf* @since 2019/4/25* 类描述:*/
@Configuration
public class CorsConfig {@Beanpublic WebMvcConfigurer corsConfigurer() {return new WebMvcConfigurer() {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**")// 它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,也可以设置为*号支持全部.allowedHeaders("*")// 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。.allowedMethods("*")// 本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求.allowedOrigins("*")// 该字段可选,用来指定本次预检请求的有效期,单位为秒.maxAge(1728000);}};}}

application.yml

spring:application:name: router-demoserver:port: 8989

验证结果

我们直接运行当前的项目,然后我们使用我们的postman工具直接访问:http://127.0.0.1:8989/menu/loadMenu我们会看到如下页面则说明我们的后端工程就构建完成了:

前端例子的实现

首先我们需要创建一个基于vue的前端工程,如果不懂的如何创建大家可以直接点击【spring boot +iview 前后端分离架构之前端工程的构建【CMD版】】去创建前端工程,创建过程如下:

创建完成以后我们直接使用我们的开发工具打开我们的前端工程,然后修改我们的package.json文件,修改完成以后代码如下:

{"name": "vue-router-demo-front","version": "1.0.0","description": "A Vue.js project","author": "林泽锋 <282245889@qq.com>","private": true,"scripts": {"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js","start": "npm run dev","build": "node build/build.js"},"dependencies": {"vue": "^2.5.2","vue-router": "^3.0.1","axios": "^0.15.3","axios-mock-adapter": "^1.16.0","mockjs": "^1.0.1-beta3"},"devDependencies": {"autoprefixer": "^7.1.2","babel-core": "^6.22.1","babel-helper-vue-jsx-merge-props": "^2.0.3","babel-loader": "^7.1.1","babel-plugin-syntax-jsx": "^6.18.0","babel-plugin-transform-runtime": "^6.22.0","babel-plugin-transform-vue-jsx": "^3.5.0","babel-preset-env": "^1.3.2","babel-preset-stage-2": "^6.22.0","chalk": "^2.0.1","copy-webpack-plugin": "^4.0.1","css-loader": "^0.28.0","extract-text-webpack-plugin": "^3.0.0","file-loader": "^1.1.4","friendly-errors-webpack-plugin": "^1.6.1","html-webpack-plugin": "^2.30.1","node-notifier": "^5.1.2","optimize-css-assets-webpack-plugin": "^3.2.0","ora": "^1.2.0","portfinder": "^1.0.13","postcss-import": "^11.0.0","postcss-loader": "^2.0.8","postcss-url": "^7.2.1","rimraf": "^2.6.0","semver": "^5.3.0","shelljs": "^0.7.6","uglifyjs-webpack-plugin": "^1.1.1","url-loader": "^0.5.8","vue-loader": "^13.3.0","vue-style-loader": "^3.0.1","vue-template-compiler": "^2.5.2","webpack": "^3.6.0","webpack-bundle-analyzer": "^2.9.0","webpack-dev-server": "^2.9.1","webpack-merge": "^4.1.0"},"engines": {"node": ">= 6.0.0","npm": ">= 3.0.0"},"browserslist": ["> 1%","last 2 versions","not ie <= 8"]
}

然后我们在前端工程的package.json目录执行以下命令:

cnpm install

编写axios工具

与后端建交互我们直接使用的是axios,直接在我们的前端工程的src目录底下创建一个lib目录,同时创建我们的通讯封装js【axios.js】代码如下:

import Axios from 'axios';class httpRequest {constructor() {this.options = {method: '',url: ''};// 存储请求队列this.queue = [];}// 销毁请求实例destroy(url) {delete this.queue[url];const queue = Object.keys(this.queue);return queue.length;}// 请求拦截interceptors(instance, url) {// 添加请求拦截器instance.interceptors.request.use(config => {return config}, error => {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器instance.interceptors.response.use((res) => {let {data} = res;return data;}, (error) => {message.error('服务内部错误');// 对响应错误做点什么return Promise.reject(error);})}// 创建实例create() {let conf = {baseURL: "http://127.0.0.1:8989/",timeout: 5000,headers: {'Access-Control-Allow-Origin': '*','Content-Type': 'application/x-www-form-urlencoded; charset=utf-8','X-URL-PATH': location.pathname}};return Axios.create(conf);}// 请求实例request(options) {let instance = this.create();this.interceptors(instance, options.url);options = Object.assign({}, options);this.queue[options.url] = instance;return instance(options);}
}export default httpRequest;

接着封装【api.request.js】代码如下:

import HttpRequest from './axios';
const axios = new HttpRequest();
export default axios;

最后编写接口调用通用工具【base.js】代码如下:

import axios from './api.request';
import qs from 'qs';
let Axios;if (process.env.NODE_ENV === 'mock') {Axios = require('axios');Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
}export function fetch(url, params = {}) {if (process.env.NODE_ENV === 'mock') {return new Promise((resolve, reject) => {Axios.post(url, params).then(response => {resolve(response.data);}).catch((error) => {reject(error);})})} else {return new Promise((resolve, reject) => {axiosPost(url,params,resolve)});}
}// 递归调用,保证在token过期刷新token的时候可以实现请求的二次发送
function axiosPost(url,params,resolve){axios.request({url: url,data: qs.stringify(params),method: 'post'}).then(res => {resolve(res);})
}

编写API接口

在src目录底下创建一个api/sys/menu目录,然后创建一个api.menu.js代码如下:

import {fetch} from '../../../lib/base';// 获取菜单数据
export const loadMenu = params => {return fetch('/menu/loadMenu',params);
};

编写测试页面

接着我们需要在src底下创建一个iview文件夹,同时编写三个测试页面,分别是1.vue、2.vue和3.vue,代码如下所示:

1.vue

<template><div><div>这是第一个页面</div><div><button @click="handleClick('hello2')">第二个页面</button><button @click="handleClick('hello3')">第三个页面</button></div></div>
</template>
<script>export default {name: 'hello1',methods:{handleClick(key) {this.$router.push({name: key}).catch(err => {console.log('err=>', err);})}}}
</script>

2.vue

<template><div><div>这是第二个页面</div><div><button @click="handleClick('hello1')">第一个页面</button><button @click="handleClick('hello3')">第三个页面</button></div></div>
</template>
<script>export default {name: 'hello2',methods:{handleClick(key) {this.$router.push({name: key}).catch(err => {console.log('err=>', err);})}}}
</script>

3.vue

<template><div><div>这是第三个页面</div><div><button @click="handleClick('hello1')">第一个页面</button><button @click="handleClick('hello2')">第二个页面</button></div></div></template>
<script>export default {name: 'hello3',methods:{handleClick(key) {this.$router.push({name: key}).catch(err => {console.log('err=>', err);})}}}
</script>

改造App.vue实现动态加载路由

<template><div id="app"><router-view/></div>
</template><script>import {loadMenu} from './api/sys/menu/menu.api';export default {name: 'App',methods: {init() {loadMenu().then(res => {let router = [];for (let i = 0; i < res.obj.length; i++) {let item = res.obj[i];console.log('这是请求返回以后的数据', item.routerPath);item.component = resolve => require([`@/` + item.routerPath], resolve);router.push(item);}this.$router.options.routes.push(...router);this.$router.addRoutes(this.$router.options.routes);console.log("this.$router.options.routes=>", this.$router.options.routes);})},generateRoutesFromMenu (menu = [], routes = []) {}},mounted() {this.init();}}
</script><style>#app {font-family: 'Avenir', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;}
</style>

验证动态路由

最后我们就需要验证我们的动态路由是否已经OK了,启动我们的后端工程和前端工程,然后我们直接在浏览器中输入以下的地址:http://localhost:8080/#/hello1,我们会看到如下的页面则说明我们的动态路由已经搞定了。

基于vue-router的从后端动态加载菜单的实现相关推荐

  1. vue高德、谷歌地图动态加载

    vue高德.谷歌地图动态加载 前言 引入地图资源 页面使用 完整map.js 前言 因为我们这个项目,做的是国际化项目,考虑的是,在国内使用高德地图,在国外使用谷歌地图,所以在这里做了个动态引入地图, ...

  2. vue 动态修改后端请求_vue-element-admin实战 | 第二篇: 最小改动接入后台实现根据权限动态加载菜单...

    一. 前言 本篇基于 有来商城 youlai-mall微服务项目,通过对vue-element-admin的权限菜单模块理解个性定制其后台接口,实现对vue-element-admin工程几乎不做改动 ...

  3. osgi框架 android,基于OSGi的Android应用模块动态加载框架设计与实现

    摘要: 伴随着移动互联网科技水平向4G的飞跃,移动终端的使用日趋常态化,移动智能设备的普及率越来越高,得到了大量使用者的追捧.与此同时,各手机操作系统下应用商店里正充斥着琳琅满目的移动应用产品,用户对 ...

  4. react 动态修改路由_升级到 React Router 4 并实现动态加载和模块热替换

    这篇文章是升级到Webpack2坑--模块热替换失效页面不自动刷新?的后续.那篇文章主要说明了,升级到 Webpack 2 之后,通过升级webpack-dev-server和react-hot-lo ...

  5. Vue环境下el-image图片动态加载失败问题

    <div v-for="(item,index) in list" :key="index"><el-image :src="ite ...

  6. J版 OpenStack动态加载菜单图片详解

    首先,需要了解OpenStack Horizon动态加载.注册dashboard.PanelGroup.Panel的原理流程 一.这里大致说下与加载左侧菜单相关的几个文件 1. /usr/lib/py ...

  7. Vue Router系列之路由懒加载

    文章の目录 一.具体需要 3 步: 1.安装 `@babel/plugin-syntax-dynamic-import` 包. 2.在 babel.config.js 配置文件中声明该插件. 3.将路 ...

  8. Vue + Spring Boot 项目实战(十五):动态加载后台菜单

    重要链接: 「系列文章目录」 「项目源码(GitHub)」 本篇目录 前言 一.后端实现 1.表设计 2.pojo 3.菜单查询接口(树结构查询) 二.前端实现 1.后台页面设计 2.数据处理 3.添 ...

  9. 后台管理系统中的antd vue中的问题(动态加载列表a-select-option,后台管理中a-selct-option的回显)

    一.antd vue中的a-select的动态加载列表a-select-option 1.既然是动态加载选择框的列表a-select-option,必然存在获取后台数组,并循环 后台数组格式: 数组( ...

最新文章

  1. Activiti——流程执行历史记录(七)
  2. puppet 连载二:服务端和客户端安装(ActiveMQ、MCollective)
  3. 用什么方法才能测量出无线发射模块的功率呢?
  4. 女子将车垂直开上标志杆,警方回应...
  5. hdu-5867 Water problem(水题)
  6. javascript执行机制
  7. ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp
  8. dll加载失败,返回126错误
  9. 数字电路技术可能出现的简答题_数字电子技术基础(Ⅰ)-中国大学mooc-试题题目及答案...
  10. 东华大学计算机专业在哪个校区,东华大学有几个校区及地址
  11. 红警安装中出现的问题 win10,黑屏和无法联机对战(缺少ipx协议)的问题。
  12. 超简单的将python文件改成可以运行的exe方法
  13. 苹果手机充电口接触不良怎么办_苹果连充电口都要干掉?
  14. 沉浸其境,共赴云栖数智硬核美学
  15. 【MATLAB】通信信号调制通用函数 — 傅里叶逆变换
  16. 微信步数日历打卡小程序
  17. 求n个整数的平均值与中位数
  18. 作为一个入门编程小白的感触
  19. VirtualBox下Ubuntu 20.04全屏显示
  20. java图片加气泡文字,动态图片加气泡文字 微信动态图片加文字教程

热门文章

  1. 使用opencv的viz模块显示3d点云
  2. 节点污点 Taint 和容忍度 Toleration在生产中的使用
  3. 漫画英语作文怎么写 计算机,漫画类的英语作文怎么写
  4. 一颗芯片是怎样诞生的
  5. 使用赫夫曼编码进行解码
  6. 面向对象——依赖倒转原则和里氏代换原则
  7. 想健身该怎么做?要练出肌肉需要多久?
  8. 全景分割相关论文写作与准备笔记
  9. 小程序报错 TypeError: Cannot read property getPreloadAdUnitIds ?
  10. 磁条卡芯片卡读写器|写卡器MCR200的安装与操作说明