新手福利,通过一个简单示例快速上手Spring Boot+Vue前后端分离

  • 2019 年 10 月 29 日
  • 筆記

Spring Boot + Vue 前后端分离的开发方式现在真的是火的不得了,之前楠哥写过一篇前后端分离的教程以及一篇用 Vue + Element UI 搭建后台管理系统界面的教程:

1、还搞不明白前后端分离?看完这篇文章,小白也能马上学会

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 前后端分离的开发,很简单吧,你学会了吗