前后端分离开发模式

什么是前后端分离?

前端指的就是 以html为基础的一众浏览器页面技术,负责显示数据,提供一些供给用户使用的功能,像是提交表单等。

后端指的就是对数据接收,处理,储存的技术,并基于此进行扩展的其他一众技术,比如:java、scala

假如你刚刚成为jsp的忠实粉丝,习惯了既写前端,又写后端的生活,又突然看到我这个博客,是不是觉得很诧异:老子既写前端又会后端岂不是更厉害,这是事实。

那:

前后端分离优点是什么?

  • 前后端可以身心愉快地专注于各自擅长的领域
  • 避免后端写前端代码(基本上1天时间,20%写后端代码,80%写页面…)
  • 前端配置后端代码运行环境(简直是要疯… 装一堆环境,而且有些开发环境是windows,前端是macos,装环境就要装好几天)
  • 避免前后端打架,推诿,甩锅….
  • 提高开发效率
  • 分离有助于前端大放异彩,后端专注于三高(高并发、高性能、高可用)
  • 太多了….

前后端分离缺点是什么?

  1. 当接口改变的时候,非常麻烦
  2. 需要前后端人员联调–联调开发的时间(开发+测试+联调)占项目的 15%–60%

然而这些缺点并不能盖得过上面耀眼的众多优点,你已经知道了优点与缺点了吧(●ˇ∀ˇ●),那为什么前后端分离便昭然若揭

我们如何前后端分离开发?

职责

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

流程

  • 后端编写和维护接口文档,在 API 变化时更新接口文档
  • 后端根据接口文档进行接口开发
  • 前端根据接口文档进行开发 + Mock平台
  • 开发完成后联调和提交测试

推荐接口规范工具:postman

后端开发的时候会拟定一个规范文档,将数据各种返回的状态做详细介绍,方便前端调试

原则

  • 接口返回数据即显示:前端仅做渲染逻辑处理;
  • 渲染逻辑禁止跨多个接口调用;
  • 前端关注交互、渲染逻辑,尽量避免业务逻辑处理的出现;
  • 请求响应传输数据格式:JSON,JSON数据尽量简单轻量,避免多级JSON的出现;

json是现在最流行的数据传递方式

数据格式

请求格式–统一的数据请求格式

get http://localhost:8080/user/1 http://localhost:8080/user?id=1

post请求 http://localhost:8080/user

put请求 http://localhost:8080/user

delete请求 http://localhost:8080/user/1

这就是所谓的基本增删改查一套操作,对应着restful风格的四种请求类型

响应格式–统一的返回结果

  • 基本格式

    1
    2
    3
    4
    {
    "code": 20000,
    "msg": "登录成功/修改失败/删除成功"
    }

    code : 请求处理状态(可以根据业务自行添加)

    • 20000 成功
    • 20001 失败

    msg:请求处理消息(可以根据业务自行添加)

  • 响应实体格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    "code": 20000,
    "msg": "success",
    "data": [
    "user": {
    "id": 1,
    "name": "XXX",
    "phone": "XXX"
    }
    ]
    }
  • 响应列表格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    {
    "code": 20000,
    "msg": "success",
    "data": {
    "list":[
    {
    "id": 1,
    "name": "XXX",
    "code": "XXX"
    },
    {
    "id": 2,
    "name": "XXX",
    "code": "XXX"
    },
    {
    "id": 3,
    "name": "XXX",
    "code": "XXX"
    }
    ]
    }
    }
  • 响应分页格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    {
    "code": 200,
    "msg":"success",
    "data": {
    "totalCount": 2,
    "totalPage": 1
    "pageNo": 1,
    "pageSize": 10,
    "list": [
    {
    "id": 1,
    "name": "XXX",
    "code": "H001"
    },
    {
    "id": 1,
    "name": "XXX",
    "code": "H001"
    },
    {
    "id": 1,
    "name": "XXX",
    "code": "H001"
    }
    ],
    }
    }
    • data.totalCount: 总记录数
    • data.pageNo: 当前页码
    • data.pageSize: 每页大小
    • data.totalPage: 总页数

JAVA定义统一返回结果

Result

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@Data
//@ApiModel(value = "全局统一返回结果")
public class Result {

//@ApiModelProperty(value = "是否成功")
private boolean success;

//@ApiModelProperty(value = "返回码")
private Integer code;

//@ApiModelProperty(value = "返回消息")
private String message;

//@ApiModelProperty(value = "返回数据")
private Map<String, Object> data = new HashMap<String, Object>();

private Result(){}

public static Result ok(){
Result r = new Result();
r.setSuccess(true);
r.setCode(ResultCode.OK);
r.setMessage("成功");
return r;
}

public static Result error(){
Result r = new Result();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMessage("失败");
return r;
}

public Result message(String message){
this.setMessage(message);
return this;
}

public Result code(Integer code){
this.setCode(code);
return this;
}

public Result data(String key, Object value){
this.data.put(key, value);
return this;
}

public Result data(Map<String, Object> map){
this.setData(map);
return this;
}
}

上面的规范实际上就是让我们后端开发的时候多写一些代码,方便前端调试的例子