目录

一,前后端分离介绍

二,与传统单体结构的比较

2.1,传统单体结构开发

2.2,前后端分离结构开发

三,简单实现前后端数据交互

1,准备的环境及工具

2,开发步骤

2.1,后端部分

2.2,前端部分

2.3,项目运行及实现结果

​编辑

四,简单总结


一,前后端分离介绍

所谓的前后端分离,就是将一个应用开发的前后端编码分开来写。后端负责处理,存储数据,而前端,则主要通过后端提供的数据接口,对数据进行渲染展示。通过这样前后端分工合作,使得项目的分工更加明确,大大降低了前后端代码的之间的耦合度,提高了开发效率。

二,与传统单体结构的比较

2.1,传统单体结构开发

传统的开发主要是,前端通过编写HTML静态界面,然后通过将界面内嵌到JSP中,或者使用Thymeleaf模板解析HTML静态界面。后端通过MVC架构中的DispatcherServlet将处理请求中的数据渲染ModelAndView或者Model到指定的静态界面中,从而达到前后端的整合。

整体的结构如图所示:

在这样一体化的构造中,如果后端业务逻辑需要更改或者数据获取出现问题,前端就要跟后端协调沟通,或者业务功能更加复杂时,这样一体化的弊端就会愈发明显。耦合度高,开发麻烦,严重影响开发效率。

2.2,前后端分离结构开发

采用前后端分离的方式进行开发时,前端只需要独立去编写客户端代码,后端专心于编写服务端代码,然后提供数据接口即可。前端开发人员与后端人员通过约定好接口文档(URL,数据类型,参数)就可以分别进行独立开发,并且,前端还可以进行伪数据构造进行数据展示测试。不需要完全依赖于后端,最后集成一下就能实现相应的业务功能了,从而达到前后端的解耦,大大提高开发效率。

整体结构如图所示:

这样开发以后,前后端开发人员可以更专注于自己擅长的领域,实现职责分离。

  • 前后端仅仅通过异步接口(AJAX/JSONP)来编程
  • 前后端都各自有自己的开发流程,构建工具,测试集合
  • 关注点分离,前后端变得相对独立并解耦合
前端 后端
接受,展示数据 提供数据
处理渲染逻辑 处理业务逻辑
MVVM架构 MVC架构
专注于客户端代码构造 专注于服务器代码构造

三,简单实现前后端数据交互

1,准备的环境及工具

开发准备 前端 后端
环境 node.js  jdk1.8,tomcat9,mysql8
技术集成 vue,axios,element-plus Springboot,MyBatis-plus
开发工具 Visual Studio Code IntelliJ IDEA 2022.1,Navicat Premium ,(ApiPost6)

2,开发步骤

2.1,后端部分

2.1.1,构造一个数据库,准备一张用于数据展示的数据表

create table test_user
(id         int(20) auto_increment comment '用户id'primary key,name       varchar(30) null comment '用户姓名',sex        tinyint(1)  null comment '性别(1为男,0为女)',address    varchar(45) null comment '用户地址',createTime datetime    null,constraint id_indexunique (id) comment 'id为唯一索引'
);

构建存储过程快速插入100条数据,详情方法查看如何使用存储过程快速插入数据

2.1.2,在IDEA里创建一个SpringBoot项目,并导入相关依赖

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--        MP代码生成器依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version></dependency><!--        3.0版本的swagger依赖--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.5</version></dependency>

2.1.3,配置yml文件里面的数据库配置

server:port: 8090
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456username: rooturl: jdbc:mysql://localhost:3306/db_user?&useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTCmvc:format:date-time: yyyy-MM-dd HH:mmmybatis-plus:mapper-locations: classPath*:/mapper/*.xmlconfiguration:map-underscore-to-camel-case: false # 禁止大写变小写时自动添加下划线log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.1.4,通过MyBatis-plus代码生成器,直接构建基础的项目结构(pojo,service,dao,serviceImpl,controller),通过MyBatis-Plus自动生成代码后,我们基本的一些代码全部省去了,包括业务需要的简单增删改查也全部轻松自动搞定。需要注意的是,自动生成的代码并不能完全适用于我们所有业务,如果业务需求有变,还是需要我们自己手动编写动态SQL,不要过于依赖框架哦~~~~这样后端基本的框架就搭建成功了

2.1.5, 考虑到在前后端数据对介绍会涉及到跨域问题,接口文档对接问题,因此需要简单编写一下跨域,Swagger的配置类。

package com.yy.Config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;/*** @author young* @date 2022/8/23 17:33* @description: 跨域配置*/
@Configuration
public class CorsConfig {/*** 最大有效时长*/private static final  long MAX_TIMEOUT=24*60*60;@Beanpublic CorsConfiguration corsConfiguration(){CorsConfiguration corsConfiguration = new CorsConfiguration();corsConfiguration.setMaxAge(MAX_TIMEOUT);//设置访问源请求头corsConfiguration.addAllowedHeader("*");//设置访问源地址corsConfiguration.addAllowedOrigin("*");//设置访问源请求方法corsConfiguration.addAllowedMethod("*");return corsConfiguration;}/*** 设置跨域配置* @return*/@Beanpublic CorsFilter corsFilter(){UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**",corsConfiguration());return new CorsFilter(source);}
}
package com.yy.Config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;import java.util.ArrayList;@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {//添加分组@Beanpublic Docket docket1(){return new Docket(DocumentationType.SWAGGER_2).groupName("黎治跃");}//配置Swagger的Docket的bean实例@Beanpublic Docket docket(Environment environment){//设置要显示的Swagger环境Profiles profiles = Profiles.of("dev", "test");//获取项目环境boolean b = environment.acceptsProfiles(profiles);System.out.println(b);return new Docket(DocumentationType.SWAGGER_2).groupName("YY").apiInfo(apiInfo()).enable(b)  //是否启动Swagger,false,则浏览器无法访问Swagger.select()//RequestHandlerSelectors,配置要扫描的接口方式//basePackage指定要扫描的包//any : 扫描全部//none :不扫描//withClassAnnotation : 扫描类上的注解.apis(RequestHandlerSelectors.basePackage("com.yy.controller"))//path :过滤路径//.paths(PathSelectors.ant("/yy/**")).build();
}//配置Swagger信息=apiinfoprivate ApiInfo apiInfo(){Contact contact = new Contact("YY", "https://www.4399.com", "2463252763@qq.com");return new ApiInfo("YY的SwaggerAPI文档", "黎治跃失恋了,2022/8/19","1.0v","urn:tos",contact, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0",new ArrayList<>());}
}

为了更好地清晰的让我们获取的信息与前端统一,也可以编写一个统一返回值的类,让返回结果以自定义的JSON格式展示出来,方便我们阅读。

定义一个方便获取常量的枚举类

/*** @author young* @date 2022/8/19 21:36* @description: 响应结果枚举类*/@AllArgsConstructor
@Getter
public enum ResponseEnum {/**响应成功**/SUCCESS(200, "操作成功"),/**操作失败*/FAIL(201,"获取数据失败"),/**错误请求**/ERROR(400,"错误请求"),/**页面未找到**/NOT_FOUND(404,"页面未找到"),/**系统异常**/SYS_ERROR(-1,"系统异常"),/*信息已存在*/MSG_ERROR(409,"信息已存在");/**响应码**/private final Integer code;/** 结果 **/private  final String  resultMessage;public static ResponseEnum getResultCode(Integer code){for (ResponseEnum value : ResponseEnum.values()) {if (code.equals(value.getCode())){return value;}}return ResponseEnum.ERROR;}
/*
简单测试一下*/public static void main(String[] args) {ResponseEnum resultCode = ResponseEnum.getResultCode(100);System.out.println(resultCode);}
}

定义统一返回值的类

package com.yy.utils;
import com.yy.enums.ResponseEnum;
import lombok.Data;import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;/*** @author young* @date 2022/8/19 21:52* @description: 统一返回结果的类*/@Data
public class  R<T> implements Serializable {private static final long serialVersionUID = 56665257248936049L;/**响应码**/private Integer code;/**返回消息**/private String message;/**返回数据**/private T data;private R(){}/*** 操作成功ok方法*/public static <T> R<T> ok(T data) {R<T> response = new R<>();response.setCode(ResponseEnum.SUCCESS.getCode());response.setMessage(ResponseEnum.SUCCESS.getResultMessage());response.setData(data);return response;}/*** 编译失败方法*/public static <T> R<T> buildFailure(Integer errCode, String errMessage){R<T> response = new R<>();response.setCode(errCode);response.setMessage(errMessage);return response;}}

如果需要在后端对获取的响应数据用mybatis-plus进行分页呢。还需要配置一下mybatis-plus的配置类

/*** @author young* @date 2022/8/29 21:27* @description: MyBatis-Plus分页配置*/
@Configuration
@MapperScan("com.yy.dao")
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false// paginationInnerInterceptor.setOverflow(false);// 设置最大单页限制数量,默认 500 条,-1 不受限制// paginationInnerInterceptor.setMaxLimit(500L);interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}
}

当然,这个并不是必须的,后面在前端通过element-plus也能对数据自动进行分页展示,个人认为更加方便,没有必要在后端进行分页。如果是为了更好的按条件查询或其他之用,也可以考虑 。

2.1.6,编写controller去提取前端页面需要的数据

/*** @author young* @date 2022/8/26 9:36* @description:*/
@Slf4j
@RestController
@RequestMapping("/mysql")
public class TestUserController {private static final DateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Resourceprivate TestUserServiceImpl testUserService;
/*                            前后端分离测试                    *//*** 添加用户** @param request* @return*/@PostMapping("/addUserTo")public R<Object> addUser(HttpServletRequest request) {JSONObject object = new JSONObject();String name = request.getParameter("name").trim();String sex = request.getParameter("sex").trim();String address = request.getParameter("address").trim();String createTime = request.getParameter("createTime").trim();if ("".equals(name)) {object.put("code", 0);object.put("msg", "用户不能为空");return R.ok(object);}TestUser user = new TestUser();Date date = new Date();try {date = dataFormat.parse(createTime);} catch (Exception e) {e.printStackTrace();}user.setName(name);user.setSex(Boolean.valueOf(sex));user.setAddress(address);user.setCreateTime(date);try {boolean add = testUserService.save(user);if (add) {object.put("code", 1);object.put("success", true);object.put("msg", "添加成功");object.put("type", "success");return R.ok(object);} else {object.put("code", 0);object.put("success", false);object.put("msg", "添加失败");object.put("type", "error");return R.buildFailure(ResponseEnum.FAIL.getCode(), ResponseEnum.FAIL.getResultMessage());}} catch (DuplicateKeyException e) {object.put("code", 2);object.put("success", false);object.put("msg", "用户已存在");object.put("type", "error");return R.buildFailure(ResponseEnum.MSG_ERROR.getCode(), ResponseEnum.MSG_ERROR.getResultMessage());}}/*** 前端获取所有数据** @return*/@GetMapping("/getAllTo")public R<List<TestUser>> allUser() {return R.ok(testUserService.list());}/*** 返回指定id的用户** @param request* @return*/@GetMapping("/getById")public R<TestUser> getById(HttpServletRequest request) {String id = request.getParameter("id");TestUser user = testUserService.getById(id);return R.ok(user);}/*** 删除用户** @param request* @return*/@DeleteMapping("deleteUserTo")public R<Boolean> deleteUserTo(HttpServletRequest request) {String id = request.getParameter("id");return R.ok(testUserService.removeById(id));}/*** 更新用户信息** @param request* @return*/@PostMapping("/updateUserTo")public R<Object> updateUserTo(HttpServletRequest request) {JSONObject jsonObject = new JSONObject();String id = request.getParameter("id").trim();String name = request.getParameter("name").trim();String sex = request.getParameter("sex").trim();String address = request.getParameter("address").trim();String createTime = request.getParameter("createTime").trim();TestUser testUser = new TestUser();Date date = new Date();try {date = dataFormat.parse(createTime);} catch (Exception e) {e.printStackTrace();}testUser.setId(Integer.parseInt(id));testUser.setName(name);testUser.setSex(Boolean.valueOf(sex));testUser.setAddress(address);testUser.setCreateTime(date);boolean res = testUserService.updateById(testUser);if (res) {jsonObject.put("code", 1);jsonObject.put("msg", "修改成功!");R.ok(jsonObject).toString();return R.ok(jsonObject);} else {jsonObject.put("code", 0);jsonObject.put("msg", "修改失败");return R.buildFailure(ResponseEnum.FAIL.getCode(), ResponseEnum.FAIL.getResultMessage());}}
}

最后,通过接口测试工具(Swagger,ApiPost,Postman都可)对我们写的数据接口测试一下,数据返回值符合预期的话,那么后端代码就该一段落了!

2.2,前端部分

前端主要通Vue框架构建项目,主要是对客户端界面进行构造。由于笔者对于前端基础不怎么好,因此主要用Element-Plus进行界面构造,axios解决前后端交互。vue使用的是vue3,但是函数方法上仍旧采用的vue2的形式,主要实现过程如下:

2.2.1,在main.js上全局配置需要使用到的插件

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import * as ELIcons from '@element-plus/icons-vue'
// 配置路由器
import router from './router'import './assets/global.css'
createApp(App).use(router).use(ELIcons).use(ElementPlus,{size:'small'}).mount('#app')

2.2.2,封装aixos请求后端的请求方式request.js

import axios from 'axios'
import {BASE_URL} from '../util/name'axios.defaults.timeout = 5000 // 超时时间设置
axios.defaults.baseURL = BASE_URL
// Content-Type 响应头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'// response 拦截器
// 可以在接口响应后统一处理结果
axios.interceptors.response.use(response => {let res = response.data;// 如果是返回的文件if (response.config.responseType === 'blob') {return res}// 兼容服务端返回的字符串数据if (typeof res === 'string') {res = res ? JSON.parse(res) : res}return res;},error => {console.log('err' + error) // for debugreturn Promise.reject(error)}
)/*** 封装get方法* @param url* @param data* @returns {Promise}*/export function get (url, params = {}, responseType = 'json') {return new Promise((resolve, reject) => {axios.get(url, {params: params,responseType}).then(response => {resolve(response.data)}).catch(err => {reject(err)})})}/*** 封装post请求* @param url* @param data* @returns {Promise}*/export function post (url, data = {}) {return new Promise((resolve, reject) => {axios.post(url, data).then(response => {resolve(response.data)}, err => {reject(err)})})}/*** 封装delete请求* @param url* @param data* @returns {Promise}*/export function deletes (url, data = {}) {return new Promise((resolve, reject) => {axios.delete(url, data).then(response => {resolve(response.data)}, err => {reject(err)})})}

2.2.3,封装前后端api对应的请求接口index.js

import {get,post,deletes} from '../util/request'
const HttpManager={// 前端用到的函数            后端对应的接口//返回所有用户 getAllUser: () => get(`mysql/getAllTo`),// 返回指定ID的用户getUserOfId: (id) => get(`mysql/getById?id=${id}`),// 添加用户addUser: (params) => post(`mysql/addUserTo`, params),// 更新用户信息updateUserMsg: (params) => post(`mysql/updateUserTo`, params),// 删除用户deleteUser: (id) => deletes(`mysql/deleteUserTo?id=${id}`),//模糊查询likeSelect:(params)=>get(`mysql/likeSelect`,params)
}
export {HttpManager}

2.2.4,提取公共的methos,放在mixins中,这样就能将共用的功能以对象的方式传入 mixins选项中,当组件使用 mixins对象时所有mixins对象的选项都将被混入该组件本身的选项中来,这样就可以提高代码的重用性,使代码保持干净和易于维护。

<!--mixins/index.js-->export const mixin = {methods: {// 获取要删除列表的idhandleDelete (id) {this.idx = idthis.delVisible = true},// 获取批量要删除的列表handleSelectionChange (val) {this.multipleSelection = val},// 批量删除delAll () {console.log("执行该方法")for (let item of this.multipleSelection) {this.handleDelete(item.id)this.deleteRow(item.id)}this.multipleSelection = []},getTime (val) {let time = String(val).match(/[0-9-]+(?=\s)/)return Array.isArray(time) ? time[0] : time},changeSex (value) {if (value === false) {return '女'} else if (value === true) {return '男'}},toggleSex (value) {if (value === '女') {return false} else if (value === '男') {return true}},// 更新图片handleAvatarSuccess (res, file) {if (res.code === 1) {this.imageUrl = URL.createObjectURL(file.raw)this.getData()this.$notify({title: '上传成功',type: 'success'})} else {this.$notify({title: '上传失败',type: 'error'})}},beforeAvatarUpload (file) {const isJPG = (file.type === 'image/jpeg') || (file.type === 'image/png')const isLt2M = file.size / 1024 / 1024 < 2if (!isJPG) {this.$message.error('上传头像图片只能是 JPG 格式!')}if (!isLt2M) {this.$message.error('上传头像图片大小不能超过 2MB!')}return isJPG && isLt2M},}
}

2.2.5,抽离出侧边栏组件AsiderBody,头部组件HeaderBody,直接从官网提取,并进行简单的样式更改

<!--HeaderBody.vue -->
<template><!-- 收缩 --><div  style="font-size: 14px;line-height: 60px; display: flex"><div style="flex: 1;font-size: 20px"><el-icon style="cursor: pointer" @click="collapse"><Fold /></el-icon></div><div class="toolbar" style="width: 70px"><el-dropdown style="cursor: pointer"><el-icon style="margin-right: 8px; margin-top: 24px"><ArrowDown/></el-icon><template #dropdown><el-dropdown-menu><el-dropdown-item>个人信息</el-dropdown-item><el-dropdown-item>退出</el-dropdown-item></el-dropdown-menu></template></el-dropdown><span>张三</span></div></div>
</template><script>
import {Fold,ArrowDown} from '@element-plus/icons-vue'
export default {
name:'HeaderBody',
components:{
Fold,
ArrowDown
},
props:['collapse'],
}
</script><style></style>
<!--AsiderBody.vue -->
<template><el-scrollbar style="background-color: #304156"><el-menu:default-openeds="['1', '3']"background-color="#304156"text-color="rgb(91, 119, 211)"active-text-color="#ffd04b"overflow="hidden":collapse="isCollapse":collapse-transition="false"router><!-- 添加头部log --><div style="height: 60px; line-height: 60px; text-align: center"><imgsrc="../assets/yp.png"style="width: 20px; posotion: relative; top: 5px; margin-right: 5px"/><b style="color: white" v-show="logoTestShow">后台管理系统</b></div><el-sub-menu index="/"><template #title><el-icon><Menu /></el-icon><span>导航 主页</span></template><el-menu-item-group><template #title>分组 1</template><el-menu-item index="/user">用户管理 1</el-menu-item><el-menu-item index="/about">关于-登录</el-menu-item></el-menu-item-group><el-menu-item-group title="分组 2"><el-menu-item index="1-3">选项 3</el-menu-item></el-menu-item-group><el-sub-menu index="1-4"><template #title>选项4</template><el-menu-item index="1-4-1">选项 4-1</el-menu-item></el-sub-menu></el-sub-menu><el-sub-menu insdex="2"><template #title><el-icon><Message /></el-icon><span>导航 Two</span></template><el-menu-item-group><template #title>分组 1</template><el-menu-item index="2-1">选项 1</el-menu-item><el-menu-item index="2-2">选项 2</el-menu-item></el-menu-item-group><el-menu-item-group title="Group 2"><el-menu-item index="2-3">选项 3</el-menu-item></el-menu-item-group><el-sub-menu index="2-4"><template #title>选项 4</template><el-menu-item index="2-4-1">选项 4-1</el-menu-item></el-sub-menu></el-sub-menu><el-sub-menu index="3"><template #title><el-icon><House /></el-icon><span>导航 Three</span></template><el-menu-item-group><template #title>分组 1</template><el-menu-item index="3-1">选项 1</el-menu-item><el-menu-item index="3-2">选项 2</el-menu-item></el-menu-item-group><el-menu-item-group title="Group 2"><el-menu-item index="3-3">选项 3</el-menu-item></el-menu-item-group><el-sub-menu index="3-4"><template #title>选项 4</template><el-menu-item index="3-4-1">选项 4-1</el-menu-item></el-sub-menu></el-sub-menu></el-menu></el-scrollbar>
</template><script>
import { Menu, Message, House } from "@element-plus/icons-vue";
export default {name: "AsiderBody",props: {isCollapse: Boolean,logoTestShow: Boolean,},components: {Menu,Message,House,},
};
</script><style>
</style>

2.2.6,构建主要的信息展示界面,这样只有el-main里面的内容会随路由的改变而切换,而侧边栏AsiderBody,头部栏HeaderBody不会改动,从而实现组件复用。

<!--HomePage.vue-->
<template><el-container class="layout-container-demo" style="height: 100vh"><el-aside :width="sideWidth + 'px'" background-color="rgb(238,241,246)"><asider-body :isCollapse="isCollapse" :logoTestShow="logoTestShow" :collapse="collapse"/></el-aside><el-container><el-header style="border-bottom:1px solid #ccc"><HeaderBody :collapse="isCollapse"/></el-header><el-main style="margin-left: 40px"><router-view/></el-main></el-container></el-container>
</template><script>import { mixin } from "../mixins/index";
import AsiderBody from "@/components/AsiderBody.vue"
import HeaderBody from "@/components/HeaderBody.vue"
export default {name: "HomePage",mixins: [mixin],components: {AsiderBody,HeaderBody},data() {return {isCollapse: false,sideWidth: 200,logoTestShow: true,}},methods:{// 收缩侧边栏collapse() {this.isCollapse = !this.isCollapse;if (this.isCollapse) {(this.sideWidth = 64), (this.logoTestShow = false);} else {(this.sideWidth = 200), (this.logoTestShow = true);}},
}
}
</script><style scoped>
.layout-container-demo .el-header {position: relative;background-color: var(--el-color-primary-light-7);color: var(--el-text-color-primary);
}
.layout-container-demo .el-aside {color: var(--el-text-color-primary);background: var(--el-color-primary-light-8);
}
.layout-container-demo .el-menu {border-right: none;
}
.layout-container-demo .el-main {padding: 0;
}
.layout-container-demo .toolbar {display: inline-flex;align-items: center;justify-content: center;height: 100%;right: 20px;
}</style>

2.2.7,用户信息展示界面构造,以及相关函数编写调用

<!--UserMsg.vue-->
<template><div><div style="margin-top: 20px; text-size: 20px"><el-breadcrumb separator="/"><el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item><el-breadcrumb-item><a href="/user">用户信息</a></el-breadcrumb-item></el-breadcrumb></div><div style="margin-top: 10px"><el-inputv-model="select_word"placeholder="筛选相关用户"style="width: 200px"><template #suffix><el-icon><Search /></el-icon></template></el-input><el-buttonroundcolor="#db6b2b"class="ml-5"plain@click="reSet">重置</el-button></div><div style="padding: 10px 0; display: inline-flex"><el-button type="primary" @click="delAll">批量删除<el-icon><DeleteFilled /></el-icon></el-button><el-buttontype="primary"class="mr-5"@click="centerDialogVisible = true">添加用户<el-icon><Plus /></el-icon></el-button><el-uploadref="upload"action="#"accept=".xlsx, .xls":auto-upload="false":on-change="uploadFile":show-file-list="false"><el-button type="primary" class="ml-5">导入数据<el-icon><Download /></el-icon></el-button></el-upload><el-buttontype="primary"style="margin-left: 10px"@click="exportE()">导出数据<el-icon><Upload /></el-icon></el-button></div><el-table:data="datas"style="text-align: center"borderstripe@selection-change="handleSelectionChange"><el-table-columntype="selection"width="40"align="center"></el-table-column><el-table-column prop="id" label="ID" width="80px" align="center" /><el-table-columnprop="createTime"label="日期"width="240px"align="center"><!-- <template v-slot="scope"><div>{{ getTime(scope.row.createTime) }}</div></template> --></el-table-column><el-table-columnprop="name"label="姓名"width="200px"align="center"/><el-table-column prop="sex" label="性别" width="200px" align="center"><!--  eslint-disable-next-line --><template v-slot="scope"><div>{{ changeSex(scope.row.sex) }}</div></template></el-table-column><el-table-columnprop="address"label="地址"width="200px"align="center"/><el-table-column label="操作" align="center"><!-- 解决scope没被使用的问题 --><!--  eslint-disable-next-line --><template v-slot="scope"><el-buttontype="success"class="mr-5"@click="handleEdit(scope.row)">编辑<el-icon><Edit /></el-icon></el-button><el-buttontype="danger"class="mr-5"@click="handleDelete(scope.row.id)">删除<el-icon><DeleteFilled /></el-icon></el-button><el-button type="warning" class="mr-5">权限<el-icon><ElementPlus /></el-icon></el-button></template></el-table-column></el-table><div style="padding: 10px 0"><el-pagination@current-change="handleCurrentChange"backgroundlayout="total, prev, pager, next":current-page="currentPage":page-size="pageSize":total="tableData.length"></el-pagination></div><!--添加新用户--><el-dialogtitle="添加用户"v-model="centerDialogVisible"width="400px"center><el-form:model="registerForm"status-icon:rules="rules"ref="registerForm"label-width="70px"class="demo-ruleForm"><el-form-item label="用户名" prop="name" size="small"><el-inputv-model="registerForm.name"placeholder="用户名"></el-input></el-form-item><el-form-item label="性别" size="small"><el-radio-group v-model="registerForm.sex"><el-radio :label="false">女</el-radio><el-radio :label="true">男</el-radio></el-radio-group></el-form-item><el-form-item label="创建时间" prop="createTime" size="small"><el-date-pickertype="date"placeholder="选择日期"v-model="registerForm.createTime"style="width: 100%"></el-date-picker></el-form-item><el-form-item label="地址" prop="address" size="small"><el-inputv-model="registerForm.address"placeholder="地址"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button size="small" @click="centerDialogVisible = false">取 消</el-button><el-button type="primary" size="small" @click="addPeople">确 定</el-button></span></template></el-dialog><!-- 编辑弹出框 --><el-dialog title="编辑" v-model="editVisible" width="400px"><el-form ref="form" :model="form" label-width="60px"><el-form-item label="用户名" size="small"><el-input v-model="form.name" :disabled="true"></el-input></el-form-item><el-form-item label="性别" size="small"><el-radio-group v-model="form.sex"><el-radio :label="false">女</el-radio><el-radio :label="true">男</el-radio></el-radio-group></el-form-item><el-form-item label="创建日期" prop="createTime" size="small"><el-date-pickertype="date"placeholder="选择日期"v-model="form.createTime"style="width: 100%"></el-date-picker></el-form-item><el-form-item label="地址" size="small"><el-input v-model="form.address"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button size="small" @click="editVisible = false">取 消</el-button><el-button type="primary" size="small" @click="saveEdit">确 定</el-button></span></template><!-- 删除提示框 --></el-dialog><DelDialog:delVisible="delVisible"@deleteRow="deleteRow"@cancelRow="delVisible = $event"></DelDialog></div>
</template><script>
import { HttpManager } from "../api/index";
import { getDateTime } from "../util/DataUtil";
import DelDialog from "@/components/DelDialog.vue";
import { mixin } from "../mixins/index";
import {Plus,Upload,Download,Search,Edit,DeleteFilled,ElementPlus,
} from "@element-plus/icons-vue";
export default {
name:'UserMsg',
mixins: [mixin],
components:{Plus,Upload,Download,Search,Edit,DeleteFilled,ElementPlus,DelDialog,
},data() {return {tableData: [],multipleSelection: [], // 记录要删除的用户信息centerDialogVisible: false,editVisible: false, // 显示编辑框delVisible: false, // 显示删除框select_word: "", // 记录输入框输入的内容pageSize: 10, // 页数currentPage: 1, // 当前页idx: -1, // 记录当前点中的行tempDate: [],is_Search: false,excelVisible: false,registerForm: {// 添加用户name: "",sex: "",createTime: "",address: "",},form: {// 记录编辑的信息id: "",name: "",sex: "",createTime: "",address: "",//updateTime: ''},};},computed: {// 计算当前表格中的数据datas() {return this.tableData.slice((this.currentPage - 1) * this.pageSize,this.currentPage * this.pageSize);},},//模糊查询watch: {select_word() {if (this.select_word === "") {this.tableData = this.tempDate;} else {this.tableData = [];for (let item of this.tempDate) {if (item.name.includes(this.select_word) ) {this.tableData.push(item);}}}},},created() {//获取分页数据信息this.getData();},methods: {// 获取用户信息getData() {this.tableData = [];this.tempDate = [];HttpManager.getAllUser().then((res) => {this.tableData = res;this.tempDate = res;this.currentPage = 1;console.log(this.tempDate)});},//重置搜索信息reSet() {this.select_word = "";this.getData();},// 添加用户addPeople() {let createTime = getDateTime(this.registerForm.createTime);let params = new URLSearchParams();params.append("name", this.registerForm.name);params.append("sex", this.registerForm.sex);params.append("createTime", createTime);params.append("address", this.registerForm.address);HttpManager.addUser(params).then((res) => {if (res.code === 1) {this.getData();this.registerForm = {};this.$notify({title: "添加成功",type: "success",});} else {this.$notify({title: "添加失败",type: "error",});}}).catch((err) => {console.error(err);});this.centerDialogVisible = false;},// 分页// handleSizeChange(pageSize) {//   console.log(`每页 ${pageSize} 条`);//   this.pageSize = pageSize;//  // this.getData();// },handleCurrentChange(val) {this.currentPage = val;},// 编辑handleEdit(row) {this.idx = row.id;this.form = {id: row.id,name: row.name,sex: row.sex,createTime: row.createTime,address: row.address,};this.editVisible = true;},// 保存编辑saveEdit() {let datetime = getDateTime(new Date(this.form.createTime));let params = new URLSearchParams();params.append("id", this.form.id);params.append("name", this.form.name);params.append("sex", this.form.sex);params.append("createTime", datetime);params.append("address", this.form.address);HttpManager.updateUserMsg(params).then((res) => {if (res.code === 1) {this.getData();this.$notify({title: "修改成功",type: "success",});} else {this.$notify({title: "修改失败",type: "error",});}}).catch((err) => {console.error(err);});this.editVisible = false;},// 确定删除deleteRow() {HttpManager.deleteUser(this.idx).then((res) => {if (res) {this.getData();this.$notify({title: "删除成功",type: "success",});} else {this.$notify({title: "删除失败",type: "error",});}}).catch((error) => {console.error(error);});this.delVisible = false;},},
};
</script><style>
/* 分页 */
.demo-pagination-block + .demo-pagination-block {margin-top: 10px;
}
.demo-pagination-block .demonstration {margin-bottom: 16px;
}
</style>

2.2.8,简单配置一下router,这样运行项目后,localhst:8080访问进入到“/home”请求对应的组件界面(随便写一个即可)上,点击侧边栏上的用户管理就能跳转到对应的信息展示界面。

import { createRouter, createWebHistory } from 'vue-router'
import HomePage from '@/view/HomePage'const routes = [{path: '/',name: 'HomePage',component: HomePage,redirect: '/home',children: [{path: 'home',name: 'HomeTest',component: () => import('../components/HomeTest.vue')},{path: 'user',name: 'UserMsg',component: () => import('../view/UserMsg.vue')},{path: 'about',name: 'About',component: () => import('../components/AboutTest.vue')},]},
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})
export default router

2.2.9,在vue.config.js上通过chainWebpack配置后端代理地址,这样由于后端已经配置了跨域,前端就可以通过对后端的请求访问到后端对应的接口了。

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,chainWebpack: config => {config.plugin('define').tap(definitions => {Object.assign(definitions[0]['process.env'], {NODE_HOST: '"http://localhost:8090"',});return definitions;});}
})

2.3,项目运行及实现结果

前端通过 npm install 安装项目所需的依赖,然后npm run serve运行项目即可得到初始的HomePage界面。因为前端学的并不扎实,页面配色布局可能一言难尽……

展开导航主页,点击用户展示界面,得到相应界面,此时并没有数据展示,只有初始的一些界面 ,因为后端服务没有开启,数据获取不到。

运行后端项目,重新刷新一下前端界面,数据通过element-plus中的el-pagination分页插件已经实现了分页效果。

并且该能实现一些基本增删改查操作,后端数据库的信息也会相应进行更改!

至此,简单的数据交互就实现了。

四,简单总结

这个测试项目还有一些小BUG正在完善中,还有一些实现的功能并没有完全放在博客中,这篇文章仅限于展示一些数据,实现前后端数据之间的交互。后期会继续完善成为一个简易的后台管理系统,供学习练习之用,项目会陆续上传到GitHub/Gitee上……有问题的地方希望大家指正交流,共同进步。

补充:

gitee项目地址

由于项目中涉及一些mysql以及mongodb的数据库,但是考虑比较简单,只是作为数据模拟使用的,因此没有放在项目中,前端也比较较简单没有放在gitee中,大家自行构建。

Springboot+Vue实现简单的前端后分离数据交互相关推荐

  1. Springboot+vue 社团管理系统(前后端分离)

    Springboot+vue 社团管理系统(前后端分离) zero.项目功能设计图 一.数据库设计(项目准备) 1.建表 2.表目录 二.前端编写(vue) 1.搭建Vue框架 2.放入静态资源(as ...

  2. springboot+vue搭建简单的聊天网站,从0到上线(腾讯云)

    springboot+vue搭建简单的聊天网站,从0到上线 整体架构简单梳理 云服务器 nginx的基础配置 springboot-eureka简单梳理 聊天功能实现的基础流程 ws的实现 整体架构简 ...

  3. python的前端和后端_python前端和后端数据交互,tornado框架入门,初学小试牛刀!...

    Python前端和后端是如何交互的,怎么用tornado框架快速搭建前端和后端数据交互? 前端与后端的数据交互,最常用的就是GET.POST,比较常用的用法是:提交表单数据到后端,后端返回json 前 ...

  4. 开源前端 可视化大数据交互前端动态模板

    介绍: 如今老板都很在乎公司实力形象 往往会在大厅投放展示大数据巨屏 你是否也想实现这样大数据效果展示 本次带来一套开源的前端可视化大数据交互动态模板网页前端模板,是HTML网页模板 只要稍微懂点前端 ...

  5. 前端后分离深入分析 ——浏览器渲染和服务器渲染区别

    1.为什么会有服务器渲染与客户端渲染? 首先理解服务器和浏览器客户端之间传递的是什么--HTML,CSS,JavaScript的文件以及数据载体json(xml)等文件,而文件都是静态,之所以动态是应 ...

  6. Java基于springboot+vue的电子相册管理系统 前后端分离node

    智能电子相册是一个可以永久保留记忆的东西,用户可以讲自己美好的一面展示在网络上,人更多的人了解到自己的生活,为此我们通过Java语言并结合springboot+vue开发了本次的电子相册管理系统,希望 ...

  7. 【EasyExcel】在SpringBoot+VUE项目中引入EasyExcel实现对数据的导出(封装工具类)

    在SpringBoot+VUE项目中引入EasyExcel实现导入导出 一.引入EasyExcel 通过maven引入,坐标如下: <dependency><groupId>c ...

  8. web前端与后台数据交互

    1.前端请求数据URL由谁来写? 在开发中,URL主要是由后台来写的,写好了给前端开发者.如果后台在查询数据,需要借助查询条件才能查询到前端需要的数据时,这时后台会要求前端提供相关的查询参数,这里的查 ...

  9. 前端和后端 数据交互的基本知识

    一.首先了解前端.后端.数据三者的关系 1) 前端通常是html,css,js三者构成的页面的总称.运行在客户端.以浏览器为例. 2) 后端是指后端程序.比如java,php等编写的一些服务.用来操作 ...

最新文章

  1. 对Linux文件中的多行进行注释
  2. Firefox Quantum支持跨浏览器插件架构
  3. LeetCode 92反转链表Ⅱ93复制ip地址94二叉树的中序遍历
  4. Linux基础第一章 概述
  5. zabbix server is not running the information displayed may not be current
  6. 【scala初学】scala 语法 声明
  7. gb28181的sip通信
  8. 铭瑄显卡不支持Linux,铭瑄主板bios设置显卡方法
  9. 战舰少女服务器不显示,战舰少女进不去 闪退及连接不上解决方法
  10. 一位区域销售经理百条经验手记
  11. 关于一些桌面、移动应用开发的平台
  12. axure8 Mac破解版+汉化包
  13. 目标检测_CVPR2020
  14. .NET跨平台:在CentOS上编译dnx并运行ASP.NET 5示例程序
  15. 去中心化应用程序 (dApps) 发展现状及趋势回顾
  16. WebMeeting
  17. vue-seamless-scroll公告组件的使用
  18. Leetcode 977.有序数组的平方
  19. RT5350 openwrt添加Reset按键,实现短按重启系统,长按复位系统
  20. 桌面壁纸被计算机管理员禁用,更改桌面背景时显示已经被系统管理员禁用,这种情况要怎么处理...

热门文章

  1. 七月算法深度学习笔记4 -- CNN与常用框架
  2. 帕斯卡三角形html,数学之美:杨辉三角(帕斯卡三角)的奇特性质
  3. 一刀工具箱- 在线AI智能写诗工具
  4. Android安装apk应用的时候出现INSTALL_FAILED_SHARED_USER_INCOMPATIBLE如何解决
  5. 阿里孤尽:Code Review 是一场苦涩但有意思的修行
  6. 【H5+ Quick-cocos2dx整合】之iOS 三 集成Quick-Cocos2dx SDK
  7. mysql导出表结构及数据的三种方法
  8. 谢国忠:下一场金融风暴将在6月左右开始
  9. openGauss数据库源码解析系列文章——openGauss开发快速入门(二)
  10. Linux内核之vmlinux与vmlinuz