零、前言
之前学过ThinkPHP,在初学的时候,几乎没有任何基础。当时被这种MVC分离的框架搞晕了,都不知道数据是怎么在各层之间传递的。
后来发现,搞懂MVC的关键,就是理解数据流。当时我还做了一张很大的图片,记录了MVC和数据库之间传值的方式和CRUD的过程。(由于这张图片是很久以前制作的,可能不完全正确)
当把数据流融会贯通之时,就是开悟之日。
一、由浅入深,分析流程
浏览器、前后台、数据库
先从整体上分析浏览器、前后台、数据库之间的数据流:
上图只是为了搞清楚数据在各个部门间的传递过程,图中的前台代码和后台代码实际上都是MVC,只是为了方便理解,在图中简化了。
MVC
接下来,展开它们:
解释数据流
用文字来解释一下上图的数据流:
- 用户通过浏览器Url向前台发起请求
- 前台服务器返回前台程序
- 执行前台程序
- 前台C层通过Url请求后台C层的方法
- 后台C层调用服务层方法
- 后台服务层调用仓库的接口
- 仓库通过SQL请求数据库
- 数据库返回数据给仓库
- 后台仓库接收数据,返回给后台服务层
- 服务层把数据转换成对象,返回给后台C层
- 后台C层把对象转换为JSON字符串,并返回给前台C层
- 前台C层接收JSON字符串,转化为对象,渲染V层组件
- 获取返回后的组件,呈现给浏览器页面
对以上过程的一些说明:
- 前台C层只负责数据转发,前台V层是没有数据的页面模板
- 后台C层只负责数据转发,后台M层是业务逻辑的具体实现
- 前后台的交互,是通过前台C层向后台C层发起Http请求来实现的
- 后台和数据库的交互,是通过M层发起SQL语句来实现的
- 前台实体、后台实体、数据表字段始终相互对应
二、功能的具体实现
前台请求后台:HttpClient
//定义请求地址url
const url = 'http://localhost:8080/Klass';
//定义需要请求的对象
const klass = new Klass(undefined, this.name.value,
this.teacher
);
//执行http请求,并订阅
//使用观察者模式,如果成功或失败,则执行对应的代码
this.httpClient.post(url, klass)
.subscribe(() => {
console.log('保存成功');
this.router.navigateByUrl('/klass', {relativeTo: this.route});
}, (response) => {
console.log(`向${url}发起的post请求发生错误` + response);
});
前台直接显示C层变量:双花括号{{}}
//V层代码
<td>{{student.name}}</td>
前台C、V层双向数据绑定:[(ngModel)]
//C层代码
username: string;
//V层代码
<input type="text" id="username" name="username" [(ngModel)]="username">
前台表单:(ngSubmit)
//C层代码
public onSubmit(): void {
}
//V层代码
<form id="teacherAddForm" (ngSubmit)="onSubmit()">
</form>
前台按钮:(click)
//C层代码
onDelete(klass: Klass): void {
}
//V层代码
<button (click)="onDelete(klass)">删除</button>
后台C层调用服务层方法:接口
//StudentService(服务层)
Student save(Student student);
//StudentServiceImpl(接口的具体实现)
public Student save(Student student) {
return this.studentRepository.save(student);
}
//StudentController(C层,调用M层Save())
public Student save(@RequestBody Student student) {
return studentService.save(student);
}
后台向数据库请求数据:仓库
//StudentServiceImpl (服务层接口的具体实现)
public Page<Student> findAll(Pageable pageable) {
return this.studentRepository.findAll(pageable);
}
//StudentRepository (仓库:执行请求数据库的动作)
default Page findAll(String name, String sno, Klass klass, @NotNull Pageable pageable) {
Assert.notNull(pageable, "传入的Pageable不能为null");
Specification<Student> specification = StudentSpecs.containingName(name)
.and(StudentSpecs.startWithSno(sno))
.and(StudentSpecs.belongToKlass(klass));
return this.findAll(specification, pageable);
}
总结
对于这些MVC分离或者前后台分离的框架,理解它们的关键,就在于搞明白数据从哪来到哪去。这是站在一个宏观的整体的角度去看待问题。
如果不理解数据流,只关注代码本身,那么代码就只会是一个个片段,而无法在思维中连成整体。
从我一个小白的角度来看,搞明白数据流,是学习如何使用框架的良好开端。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。