前言
前面一篇主要是介绍了怎么完成axios
请求测试以及mock.js
模拟真实数据请求,前后端分离的实际应用还是得与后端进行交互,下面是关于怎么样与java
后台进行交互的介绍。
快速开始
我么将要完成Mybatis
框架的集成,并编写查询语句查询出MySQL
数据库的数据,通过controller
层的API暴露给前端,从而实现数据的交互。
首先,完成SpringBoot项目
的创建,添加mybatis、spring-web、spring-configuration、mysql、lombok依赖。
项目目录
创建完成后,项目目录如下:
<img src="https://cdn.jsdelivr.net/gh/deepinsea/cdn/img/20210119222110.png" alt="img" style="zoom: 67%;" />
一、添加依赖
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
二、编写yaml配置文件
将根目录下的application.properties
重命名为application.yml
,然后进行配置:
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库驱动
# 此處驱动有两个
# com.mysql.jdbc.Driver
# com.mysql.cj.jdbc.Driver
# MySQL5用的驱动url是com.mysql.jdbc.Driver,
# MySQL6以后用的是com.mysql.cj.jdbc.Driver。
# 使用何种驱动,根据安装MySQL的版本而定
# 下面是Mybatis下划线转驼峰
mybatis:
configuration:
map-underscore-to-camel-case: true
注意:这里如果添加了Mybatis
而不进行数据库连接池的配置,将会报出异常!
三、编写主类文件
User.java
package com.deepinsea.cors.entity;
import lombok.Data;
/**
* @author 南街北巷
* @data 2021/1/6 15:51
*/
@Data
public class User {
private int id;
private String name;
private int age;
}
注意:我们应该事先创建好MySQL数据库,设置库、表编码为utf8mb64
(为什么MySQL不用utf-8?),然后根据数据库设置的字段编写实体类!
UserMapper.java
package com.deepinsea.cors.mapper;
import com.deepinsea.cors.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author 南街北巷
* @data 2021/1/6 15:52
*/
@Mapper
@Repository
public interface UserMapper {
/**
* 根据id查询User
* @param id
* @return
*/
@Select("select * from user where id = #{id}")
User findById(int id);
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
List<User> findAll();
}
HelloController.java
package com.deepinsea.cors.controller;
import com.deepinsea.cors.entity.User;
import com.deepinsea.cors.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author 南街北巷
* @data 2021/1/6 14:44
* @RestController 等同于 @Controller+@ResponseBody
*/
@RestController
@RequestMapping("/user")
public class HelloController {
@Autowired
private UserMapper userMapper;
@PostMapping("/getBy/{id}")
public User findById(@PathVariable int id){
User user = userMapper.findById(id);
return user;
}
@PostMapping("/getAll")
public List<User> getAll(){
List<User> users = userMapper.findAll();
return users;
}
}
四、测试使用
下面以请求APIhttp://localhost:8080/user/getBy/2
为例进行测试:
[图片上传失败...(image-df850c-1648582518205)]
测试以后,发现返回JSON
字符串成功(需要自定义序列化配置、异常、枚举可以自行配置,这里仅快速集成)!
到了这里,完成了数据库到后端的数据传递,然后是后端到前端的数据传递了。
前后端交互
现在是正式集成进行后端与前端的跨域请求,因此我们需要把注释的port
与proxyTable
打开:
端口
port: 8081 # 后端端口为8080
代理配置
webpack.js
proxyTable: {
// 跨域配置
// 修改配置后如果项目启动404,记得重启项目(类似于redis,webpack是依赖配置文件启动的)
// 错误日志:xhr.js?ec6c:177 POST http://localhost:8081/user/getAll 404 (Not Found)
'/api': {
target: 'http://localhost:8080/',
changeOrigin: true,
secure: false, // 如果是https接口,需要配置这个参数
pathRewrite: {
'^/api': ''
}
}
},
上面的配置相当于将前端的请求代理为:
http://localhost:8081/api => http://localhost:8080
上面将原后端增加一个/api
路径参数,是为了隐藏真实的后端API地址,但是对于前端请求真实的API地址没有用。因此 ,我们需要在main.js
中(或者vue.config.js)设置前端跨域代理的基本路径,这样就不用编写地址时每次都加一个/api
了:
axios.defaults.baseURL = '/api' // 设置跨域代理基本路径
因为使用axios
进行过测试(后面可以集成vuex
状态管理器对axios
进行二次封装),只需要将Home.vue
中的测试API访问地址更改为后端的API地址即可:
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import MockTest from '@/components/MockTest.vue' // 这里不能使用./AxiosTest.vue的方式引入
name: 'Home',
components: { // 局部注册(引入并注册)
HelloWorld,
MockTest
},
methods: {
mockTest () {
this.$axios.post('/user/getAll') // 'https://v1.alapi.cn/api/music/search? keyword=我爱你'
.then(res => {
console.log(res) // 在控制台打印响应数据(包括响应头与响应体)
})
.catch(err => {
console.log(err)
})
}
}
</script>
注意:也可以在created()与mounted()生命周期函数里面添加该方法,将会自动执行。
测试使用
跨域配置完成了,启动项目:
[图片上传失败...(image-f6cc8f-1648582518205)]
可以看到,成功返回了后端的数据!
到这里基本可以结束了,下面是关于数组遍历出对象显示到前端的部分(主要为原理深究)
读取对象数组数据
1. 使用foreach遍历读取
首先,一般遍历Javascript
遍历实现数据的传递都是通过document.getElementById()
方法进行单向数据传递,但是对于已经默认进行了双向绑定的vue对象来说——即:在data()中声明的对象,进行数据的传递往往要考虑一个数据的状态变化性(动态变化性)。
因为获取到axios
响应体的数据一般是通过this
指针传递给vue对象的,因此this指向的对象发生变化时全局的对象的值也会随之发生改变(this相当于指针),这将会导致数组使用for循环以及其他各种遍历方式非钩子函数,遍历时新数组对象都会将原来的数组对象覆盖:
下面是使用for循环遍历等各种数组测试
Home.vue
<template>
<div class="home">
<Hello-World />
<Mock-Test />
<button @click="userTest">试试</button>
<!-- 下面两种方式进行遍历都可以,index是数组下标0,1,2...,item是id序号1,2,3... -->
<!-- 当两种标签遍历方式都存在时,只用item的遍历会报key重复错误(应该是DOM结构冲突),
因此最好使用item和index同时定位对象的遍历方式 -->
<p>{{userList}}</p>
<!-- <p v-for="item in userList" :key="item.id">{{item}}</p>
<p v-for="(item,index) in userList" :key="index">{{item.id}}</p> -->
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import MockTest from '@/components/MockTest.vue' // 这里不能使用./AxiosTest.vue的方式引入
export default {
name: 'Home',
components: {
HelloWorld,
MockTest
},
data () {
return {
userList: '' // 注意: 这里为对象数组时参数时定义为userList:[](适用于单个对象)的话会默认产生一个[]符号,
}
},
// created () { // 或created: function(),为生命周期函数created,也可以是mounted—已挂载,可在页面加载时自动执行方法
// this.userTest()
// },
methods: {
userTest () {
this.$axios.post('/user/getAll') // 'https://v1.alapi.cn/api/music/search?keyword=我爱你'
.then(res => {
// 响应体获取测试
// console.log(res)
// console.log(res.data) // 这里采用逐步分层取值的方式测试
// console.log(res.data[0])
// console.log(res.data[0].name)
// console.log(res.data[res.data[0].id].name) // 嵌套取值
// console.log(res.data.length)
// var _this = this// 这句话的位置需要注意
// console.log(this)// 依然是这个方法的对象
// console.log(_this) // 这个变成了原来的对象
// 循环取值测试(因为this的指针变化问题,不可行,因为this无论是否处于严格模式下都引用全局对象。)
for (var i = 0; i < res.data.length; i++) { // 遍历出所有对象
// 将对象合并(for循环遍历赋值、Object.assign()、序列化、浅拷贝、深拷贝(不会覆盖对象)、foreach遍历(测试成功))
// 1.遍历赋值
// this.userList = res.data[i] // 测试发现只有id为2(即:第二个对象)对象的值,说明userList的this指向被修改了
// console.log(this.userList)
// 2.Object.assign()
// var objList = Object.assign({}, res.data[i]) // 参数表示,测试时也可以直接用常数表示
// console.log(objList)
// this.userList >= objList
// 3.序列化
// const obj = JSON.parse(JSON.stringify(res.data[i]))
// this.userList = obj
// console.log(obj)
// 浅拷贝与深拷贝需要下载JQuery,暂时没有尝试
}
// 4.foreach遍历测试
// var list = ['750', '1080', '1125', '1242', '1242']
// var data = []
// Object.keys(list).forEach(key => {
// let item = {
// val: list[key]
// }
// data.push(item)
// })
// console.log(list)
// 正式使用
var list = res.data
var data = []
Object.keys(list).forEach(key => { // 因为这里使用了ES6语法中的 => 钩子函数,因此将不会
data.push(list[key])
})
this.userList = data
console.log(data)
// this.userList = res.data
// console.log(res.data)
// axios的response采用默认参数data存放数据,这里只需要传data的值就行了,
// 否则要从一个加了其他参数的多重JSON对象中取值,无异于自己设计一个JSON序列化工具
// 注意:这里res.data前不需要加this指针,因为箭头函数没有自己的this(因此本身是局部变量),
// 箭头函数里面的this是对象的指针(定义时的全局变量),如果加了this那么将获取不到res.data的值
})
.catch(err => {
console.log(err)
})
}
}
}
</script>
使用for循环里面的遍历赋值与浅拷贝都会导致最终数组元素的值发生改变,导致对象数组遍历出对象再合并出现对象覆盖的现象:
[图片上传失败...(image-56bd16-1648582518205)]
但是使用foreach
遍历搭配钩子函数就能避免这个问题:
// 4.foreach遍历测试
// var list = ['750', '1080', '1125', '1242', '1242']
// var data = []
// Object.keys(list).forEach(key => {
// let item = {
// val: list[key]
// }
// data.push(item)
// })
// console.log(list)
// 正式使用
var list = res.data
var data = []
Object.keys(list).forEach(key => { // 因为这里使用了ES6语法中的 => 钩子函数,因此将不会
data.push(list[key])
})
this.userList = data
console.log(data)
[图片上传失败...(image-da8e20-1648582518205)]
2. 使用v-for读取
对此,vue2.0
提供了默认的数组遍历方法(当然,数组还存在下标属性更新不了以及数组长度不可响应式改变的问题,具体参考issues
;这些在vue3.0
中使用proxy
代替Object.defineProperty()
时得到了解决),下面是关于vue的数组遍历方法v-for
的使用:
Home.vue
<template>
<div class="home">
<Hello-World />
<Mock-Test />
<button @click="userTest">试试</button>
<!-- 下面两种方式进行遍历都可以,index是数组下标0,1,2...,item是id序号1,2,3... -->
<!-- 当两种标签遍历方式都存在时,只用item的遍历会报key重复错误(应该是DOM结构冲突),
因此最好使用item和index同时定位对象的遍历方式 -->
<p>{{userList}}</p>
<p v-for="item in userList" :key="item.id">{{item}}</p>
<!-- 使用单item作为参数的方式最好不要获取详细到具体key的值,否则会提示错误,因此这种适合整个对象的获取 -->
<!-- <p v-for="(item,index) in userList" :key="index">{{item.id}}</p> -->
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import MockTest from '@/components/MockTest.vue' // 这里不能使用./AxiosTest.vue的方式引入
export default {
name: 'Home',
components: {
HelloWorld,
MockTest
},
data () {
return {
userList: '' // 注意: 这里为对象数组时参数时定义为userList:[](适用于单个对象)的话会默认产生一个[]符号,
}
},
// created () { // 或created: function(),为生命周期函数created,也可以是mounted—已挂载,可在页面加载时自动执行方法
// this.userTest()
// },
methods: {
userTest () {
this.$axios.post('/user/getAll') // 'https://v1.alapi.cn/api/music/search?keyword=我爱你'
.then(res => {
// 响应体获取测试
// console.log(res)
// console.log(res.data) // 这里采用逐步分层取值的方式测试
// console.log(res.data[0])
// console.log(res.data[0].name)
// console.log(res.data[res.data[0].id].name) // 嵌套取值
// console.log(res.data.length)
// var _this = this// 这句话的位置需要注意
// console.log(this)// 依然是这个方法的对象
// console.log(_this) // 这个变成了原来的对象
// 循环取值测试(因为this的指针变化问题,不可行,因为this无论是否处于严格模式下都引用全局对象。)
for (var i = 0; i < res.data.length; i++) { // 遍历出所有对象
// 将对象合并(for循环遍历赋值、Object.assign()、序列化、浅拷贝、深拷贝(不会覆盖对象)、foreach遍历(测试成功))
// 1.遍历赋值
// this.userList = res.data[i] // 测试发现只有id为2(即:第二个对象)对象的值,说明userList的this指向被修改了
// console.log(this.userList)
// 2.Object.assign()
// var objList = Object.assign({}, res.data[i]) // 参数表示,测试时也可以直接用常数表示
// console.log(objList)
// this.userList >= objList
// 3.序列化
// const obj = JSON.parse(JSON.stringify(res.data[i]))
// this.userList = obj
// console.log(obj)
// 浅拷贝与深拷贝需要下载JQuery,暂时没有尝试
}
// 4.foreach遍历测试
// var list = ['750', '1080', '1125', '1242', '1242']
// var data = []
// Object.keys(list).forEach(key => {
// let item = {
// val: list[key]
// }
// data.push(item)
// })
// console.log(list)
// 正式使用
// var list = res.data
// var data = []
// Object.keys(list).forEach(key => { // 因为这里使用了ES6语法中的 => 钩子函数,因此将不会
// data.push(list[key])
// })
// this.userList = data
// console.log(data)
this.userList = res.data
console.log(res.data)
// axios的response采用默认参数data存放数据,这里只需要传data的值就行了,
// 否则要从一个加了其他参数的多重JSON对象中取值,无异于自己设计一个JSON序列化工具
// 注意:这里res.data前不需要加this指针,因为箭头函数没有自己的this(因此本身是局部变量),
// 箭头函数里面的this是对象的指针(定义时的全局变量),如果加了this那么将获取不到res.data的值
})
.catch(err => {
console.log(err)
})
}
}
}
</script>
启动项目,成功获取到数据:
前后端数据请求成功!
小结
前后端打通了,下面就是项目UI框架的集成了,冲冲冲💪💪
我是Java白羊🐏,一只想做全栈的小羊,感谢大家的观看ヾ(◍°∇°◍)ノ゙!