# 通过一个简单示例快速上手Spring Boot+Vue前后端分离
# 前言
Spring Boot + Vue 前后端分离的开发方式现在真的是火的不得了,之前楠哥写过一篇前后端分离的教程以及一篇用 Vue + Element UI 搭建后台管理系统界面的教程:
2、动态加载router,用Vue+Element UI搭建后台管理系统界面
Vue + Element UI 并不是真正的前后端分离,它只有前端服务,并没有后端服务提供数据接口,很多小伙伴在后台留言希望楠哥能写一篇完整的 Spring Boot + Vue 前后端分离教程。大家有需求,楠哥就会尽量满足,所以今天用一个简单易懂的例子,快速教会大家如何实现 Spring Boot + Vue 的前后端分离开发。
既然是前后端分离,那么我们就分开来写两个独立的服务,首先完成后端服务,提供数据接口。然后完成前端服务通过 Ajax 调用后端接口并动态绑定数据。后端服务我们使用 Spring Boot + MyBatis,前端服务使用 Vue + Element UI。
# 后端服务
1、我们以查询图书为例,完成基于 RESTful 的接口开发,数据库建表语句如下所示。
DROP TABLE IF EXISTS `bookcase`;
CREATE TABLE `bookcase` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
LOCK TABLES `bookcase` WRITE;
INSERT INTO `bookcase` VALUES
(1,'社会'),
(2,'情感'),
(3,'国学'),
(4,'推理'),
(5,'绘画'),
(6,'心理学'),
(7,'传记'),
(8,'科技'),
(9,'计算机'),
(10,'小说');
UNLOCK TABLES;
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`author` varchar(20) DEFAULT NULL,
`publish` varchar(20) DEFAULT NULL,
`pages` int(10) DEFAULT NULL,
`price` float(10,2) DEFAULT NULL,
`bookcaseid` int(10) DEFAULT NULL,
`abled` int(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_ieh6qsxp6q7oydadktc9oc8t2` (`bookcaseid`),
CONSTRAINT `FK_ieh6qsxp6q7oydadktc9oc8t2` FOREIGN KEY (`bookcaseid`) REFERENCES `bookcase` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=74 DEFAULT CHARSET=utf8;
LOCK TABLES `book` WRITE;
INSERT INTO `book` VALUES
(1,'解忧杂货店','东野圭吾','电子工业出版社',102,27.30,9,0),
(2,'追风筝的人','卡勒德·胡赛尼','上海人民出版社',230,33.50,3,0),
(3,'人间失格','太宰治','作家出版社',150,17.30,1,1),
(4,'这就是二十四节气','高春香','电子工业出版社',220,59.00,3,1),
(5,'白夜行','东野圭吾','南海出版公司',300,27.30,4,1),
(6,'摆渡人','克莱儿·麦克福尔','百花洲文艺出版社',225,22.80,1,1),
(7,'暖暖心绘本','米拦弗特毕','湖南少儿出版社',168,131.60,5,1),
(8,'天才在左疯子在右','高铭','北京联合出版公司',330,27.50,6,1),
(9,'我们仨','杨绛','生活.读书.新知三联书店',89,17.20,7,1),
(10,'活着','余华','作家出版社',100,100.00,6,1),
(11,'水浒传','施耐庵','三联出版社',300,50.00,1,1),
(12,'三国演义','罗贯中','三联出版社',300,50.00,2,1),
(13,'红楼梦','曹雪芹','三联出版社',300,50.00,5,1),
(14,'西游记','吴承恩','三联出版社',300,60.00,3,1);
UNLOCK TABLES;
2、新建 Maven 工程,pom.xml 引入相关依赖。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<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>1.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
3、创建实体类。
@Data
public class BookCase {
private int id;
private String name;
}
@Data
public class Book {
private int id;
private String name;
private String author;
private String publish;
private int pages;
private double price;
private BookCase bookCase;
}
@Data
public class BookVO {
private Integer total;
private List<Book> data;
private Integer pageSize;
}
4、创建 Repository 接口。
public interface BookRepository {
public List<Book> find(Integer index,Integer limit);
public Integer count();
}
public interface BookCaseRepository {
public BookCase findById(Integer id);
}
5、在 resources/mapping 路径下创建 Repository 对应的 Mapper.xml。
<mapper namespace="com.southwind.repository.BookRepository">
<resultMap id="bookMap" type="Book">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="author" column="author"/>
<result property="publish" column="publish"/>
<result property="pages" column="pages"/>
<result property="price" column="price"/>
<association property="bookCase" javaType="BookCase" select="com.southwind.repository.BookCaseRepository.findById" column="bookcaseid"></association>
</resultMap>
<select id="find" resultMap="bookMap">
select * from book limit #{param1},#{param2}
</select>
<select id="count" resultType="Integer">
select count(*) from book
</select>
</mapper>
<mapper namespace="com.southwind.repository.BookCaseRepository">
<select id="findById" parameterType="java.lang.Integer" resultType="BookCase">
select * from bookcase where id = #{id}
</select>
</mapper>
6、创建 Service 接口及实现类。
public interface BookService {
public BookVO findByPage(Integer page);
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookRepository bookRepository;
private Integer limit = 10;
@Override
public BookVO findByPage(Integer page) {
Integer index = (page-1)*limit;
BookVO bookVO = new BookVO();
bookVO.setData(bookRepository.find(index,limit));
bookVO.setTotal(bookRepository.count());
bookVO.setPageSize(limit);
return bookVO;
}
}
7、创建 Handler。
@RestController
@RequestMapping("/book")
public class BookHandler {
@Autowired
private BookService bookService;
@GetMapping("/findByPage/{page}")
public BookVO findByPage(@PathVariable("page") Integer page){
return bookService.findByPage(page);
}
}
8、前端服务调用后端接口时存在跨域问题,在 Spring Boot 中可以通过实现 WebMvcConfigurer 接口来解决跨域问题。
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
9、在 resources 路径下创建配置文件 application.yml。
server:
port: 8181
spring:
datasource:
url: jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=UTF-8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:/mapping/*.xml
type-aliases-package: com.southwind.entity
logging:
level:
com.southwind.repository.*: debug
10、创建启动类 Application 并运行。
@SpringBootApplication
@MapperScan("com.southwind.repository")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
11、启动成功后使用 Postman 测试 Handler 接口,如下图所示。
端口为 8181 的后端服务创建成功,接下来完成前端服务,并调用后端服务接口 。
# 前端服务
1、创建 Vue 工程,并安装 Element UI 和 axios。
2、我们使用 Element UI 来搭建前端界面,Element UI 提供了数据表格的组件,非常简单,直接查看 Element UI 官方文档即可,官方示例代码如下所示。
<template>
<el-table :data="tableData" border style="width: 100%">
<el-table-column fixed prop="date" label="日期" width="150">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="province" label="省份" width="120">
</el-table-column>
<el-table-column prop="city" label="市区" width="120">
</el-table-column>
<el-table-column prop="address" label="地址" width="300">
</el-table-column>
<el-table-column prop="zip" label="邮编" width="120">
</el-table-column>
<el-table-column fixed="right" label="操作" width="100">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
<el-button type="text" size="small">编辑</el-button>
</template>
</el-table-column>
</el-table></template><script>
export default {
methods: {
handleClick(row) {
console.log(row);
}
},
data() {
return {
tableData: [
{date: '2016-05-03',name: '王小虎',province: '上海',city: '普陀区',address: '上海市普陀区金沙江路 1518 弄',zip: 200333},
{date: '2016-05-03',name: '王小虎',province: '上海',city: '普陀区',address: '上海市普陀区金沙江路 1518 弄',zip: 200333},
{date: '2016-05-03',name: '王小虎',province: '上海',city: '普陀区',address: '上海市普陀区金沙江路 1518 弄',zip: 200333},
{date: '2016-05-03',name: '王小虎',province: '上海',city: '普陀区',address: '上海市普陀区金沙江路 1518 弄',zip: 200333}
]
}
}
}
</script>
3、运行结果如下图所示。
4、在 Vue 工程 components 路径下创建 Table.Vue,在初始化方法中通过 axios 调用后端服务的数据接口并完成动态绑定,代码如下所示。
<template>
<div>
<div>
<el-table
:data="tableData"
border
style="width: 80%;margin-left: 100px;margin-top: 30px;">
<el-table-column
fixed
prop="id"
label="编号"
width="100">
</el-table-column>
<el-table-column
prop="name"
label="图书"
width="150">
</el-table-column>
<el-table-column
prop="author"
label="作者"
width="150">
</el-table-column>
<el-table-column
prop="publish"
label="出版社"
width="180">
</el-table-column>
<el-table-column
prop="pages"
label="总页数"
width="100">
</el-table-column>
<el-table-column
prop="price"
label="价格"
width="100">
</el-table-column>
<el-table-column
prop="bookCase.name"
label="分类"
width="120">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button type="text" size="small">修改</el-button>
<el-button type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
layout="prev, pager, next"
:total="total"
:page-size="pageSize"
@current-change="change"
>
</el-pagination>
</div>
</div>
</template>
<script>
export default {
methods: {
change(currentPage) {
this.currentPage = currentPage
const _this = this
axios.get('http://localhost:8181/book/findByPage/'+currentPage).then(function (resp) {
_this.tableData = resp.data.data
})
}
},
data() {
return {
total:0,
pageSize:5,
tableData: [],
currentPage:0
}
},
created() {
const _this = this
axios.get('http://localhost:8181/book/findByPage/1').then(function (resp) {
console.log(resp.data)
_this.pageSize = resp.data.pageSize
_this.total = resp.data.total
_this.tableData = resp.data.data
})
}
}
</script>
5、在 router.js 中设置 Table.Vue 路由,代码如下所示。
import Table from '../components/Table'
routes:[
{
path: '/table',
component: Table,
name: '查询图书'
}
]
6、在终端执行命令 npm run serve
启动 Vue 服务,打开浏览器访问 http://localhost:8080/table
,结果如下图所示。
这样我们就完成了 Spring Boot + Vue 前后端分离的开发,很简单吧,你学会了吗?