前文讲到了CSI技术,这就说明网站静态化技术的讲述已经推进到了浏览器端了即真正到了web前端的范畴了,而时下web前端技术的前沿之一就是前后端分离技术了,那么在这里网站静态化技术和前后端分离技术产生了交集,所以今天我将讨论下前后端分离技术,前后端分离技术讨论完后,下一篇文章我将会以网站静态化技术的角度回过头来重新审视下前后端分离技术,希望通过这种审视来加深我们对两套技术的理解。
前后端分离技术我个人认为是web前端被专业化以后的必由之路,而nodejs的出现是前后端分离技术的一个强兴的催化剂,原因是nodejs的出现削平了前端技术和服务端技术之间的鸿沟,使得前后端两套不同技术体系进行真正意义的解耦提供了无限的可能性。但是如果我们把nodejs技术的使用认为就是实现了前后端分离,这种理解又实在太肤浅了,下面我将讲讲我研究过的前后端分离技术方案,以及这些技术方案隐藏在背后思考,希望这些思考能给大家以一个新的思路来理解前后端分离技术。
我们要深刻理解前后端分离技术有一个重要的前提,那就是要把前后端分离技术认为是传统的web应用里的MVC设计模式的进一步演进。那么我们首先来看看MVC的定义,下面的内容摘录于维基百科的解释,具体如下:
MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。 MVC模式最早由Trygve Reenskaug在1978年提出[1] ,是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件设计模式。MVC模式的目的是实现一种动态的程式设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观。软件系统通过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员可以通过自身的专长分组: (控制器 Controller)- 负责转发请求,对请求进行处理。 (视图 View) - 界面设计人员进行图形界面设计。 (模型 Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
各类用于Web应用开发的语言里都有属于自己的MVC框架,例如本人最熟悉的服务端语言java里就有大名鼎鼎的struts2,springMVC的MVC应用框架,我早期从事java的web开发时候认为这些MVC框架都是非常的博大精深,用途广泛,但是当我逐渐转向了web前端技术开发以后又觉得这些框架的很多功能显得那么的多余和累赘,因此我曾写过一篇文章专门讨论过这些问题,该文章的名字叫做《为什么做java的web开发我们会使用struts2,springMVC和spring这样的框架?》。
其实这篇文章被写的源头就是在于我认为像struts2和springMVC这样的框架做了太多浏览器本身就可以完成的工作,例如:页面的渲染操作,因为服务端抢了浏览器端的部分工作,这其实也就等于限制了web前端技术的深入运用,像很多前端的优化技术以及很多提升用户体验的技术就很难派上用场,之所以产生这些问题,我认为传统的MVC框架本质其实是一个服务端的MVC框架,虽然MVC设计模式里的V即View视图层是想把界面开发工作专业化,让界面设计人员能专心于界面开发,但是传统的MVC框架下的View层的本质却是一个不折不扣的服务端技术。
我们以java的web开发里jsp为例,JSP全名为Java Server Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计,它是java里动态网页的技术标准,这就说明jsp虽然看起来像html,其实它并不是真正的html,它需要被java的web容器进行解析转化为浏览器可以解析的html页面,然后通过网络传输到浏览器后,浏览器才能正确的展示这个jsp页面,其他web开发语言里都有类似的动态网页技术标准,但是不管什么语言的动态网页技术标准,我们使用它时候就是让web前端技术被服务端技术所绑架,这也就是为什么每个招聘web前端工程师的岗位都要问你是否会java,php语言的源头。但是随着互联网的大发展,对web前端的要求是越来越专业化,web前端本身所包含的技术难度已经不亚于任何一个服务端语言开发难度,因此我们需要web前端更高的专业化,而不希望web前端工程师被服务端技术束缚的更多而限制了自身能力的发展,这就导致前后端分离技术的出现。
不过前后端分离技术的第一阶段倒不是从改变view层即视图层开始的,而是从连接客户端和服务端的C层即控制层开始的,控制层既要作用于客户端又要作用于服务端,如果一个功能页面是一个程序员从浏览器端一直写到模型层,控制层也就不是什么问题了,但是如果当我们想按MVC的设计思想,让界面开发人员专注于页面开发,服务端开发人员专注于服务端开发,那么这个时候控制层的归属问题就显的非常重要了。在传统的MVC框架里,因为M层和C层是使用同样的语言体系,因此我们很自然会把M层和C层的开发工作都交由服务端开发人员完成,这个决定无可厚非,但是传统的MVC框架里V层和C层其本质也是同一个技术体系下的(例如java的web开发里的jsp本质就是个servlet),因此V层和C层也是紧耦合的,因此界面开发人员开发页面时候如何没有C层支撑,那么这个页面其实是根本跑不起来的,如果前端开发人员这时候跑去写写C层即控制层的代码,这就打破了原有的横向分工,这个时候控制层的编码工作就会变得混乱而难以控制,看到这里有人一定会说既然控制层是属于服务端的,那么前端技术人员就等等服务端的开发进度,再不行就自己写个mock模拟下服务端的控制层,听到这种建议,我相信不管是前端的还是服务端的技术人员都会头脑发麻,第一反应就是这不是自找麻烦啊,还不如一个人全部搞定算了。由此第一阶段的前后端分离技术方案出现了,这个方案需要解决的问题就是如何能让web前端技术人员和web服务端技术人员协同起来工作,合理的分工,换句话说就是按web前端和web服务端角度如何能横向的分解web的开发工作。
前后端分离的第一阶段需要解决问题的核心就是控制层的归属问题,从技术角度而言就是控制层到底是应该和视图层解耦比较合理还是跟模型层解耦比较合理的问题。那么我们这里先回顾下MVC设计模式里对控制层的定义,维基百科里的定义是:
(控制器 Controller)- 负责转发请求,对请求进行处理。
不过这个解释我认为并不全面,以java的web开发里的控制层设计为例,我们发现控制层以沟通视图层和模型层的角度而言,控制层其实主要完成三项具体的工作,它们分别是:
工作一:控制层起到一个路由的作用。客户端请求到达控制层后,控制层根据请求内容将请求路由到服务端某个模型层进行处理,模型层将请求处理完毕后,会把响应结果返回给控制层,控制层在根据响应信息路由到特定的页面。
工作二:控制层起到一个报文信息格式转化的作用。这里以java的web开发为例,浏览器的数据都是以http报文形式发送给服务端,而控制层就是将http报文信息解析成java的对象,当然也可以是java的基本数据类型,然后控制层把解析好的信息传递给模型层进行处理。
工作三:传统的MVC框架里,控制层其实深入参入到了页面渲染的操作。在java的web开发里的控制层不管如何被包装,其本质就是一个servlet,而jsp页面本质也是个serlvet,因此我们可以这么理解jsp,jsp就是以页面开发的方式写java,而servlet就是以java的方式写页面,所以我们可以在servlet里以文件流的方式输出页面,也可以让servlet跳转到jsp页面。
由上面的论述里我们发现,其实传统MVC框架里控制层和模型层的联系方式相对很简单的,它们的联系主要是路由和报文格式的转化上,而控制层与视图层的联系除此之外还多了一个页面渲染,而页面渲染本身应该是属于浏览器的技术范畴,是浏览器技术不可分割的一部分,也是我上面内容里诟病传统MVC框架问题所在,如果控制层承担了页面渲染工作,那么控制层和视图层的耦合度就变得非常高,要想将其解耦是十分困难,一般只有我们打破了现有MVC框架的技术体系才能完成,相比之下,控制层与模型层的解耦就显得容易多了。那么控制层与模型层如何解耦呢?具体如下:
首先我们来解决下报文格式转化的问题,这个技术方案很简单就是借鉴http统一报文格式的特点,我们为控制层和模型层定义一套统一的报文格式,例如我们定义控制层和模型层都以map的数据类型进行数据传递,这个map里有个专门的字段用来定义被路由到的模型接口信息,有个字段专门存储需要传递的数据,具体的设计方案可以根据实际的业务需要来设计。
接下来就是路由的问题了,在解决报文格式转化问题的论述里我讲到要在统一报文格式里专门定义一个字段用来存储该数据到底路由到哪个模型进行处理,不过这个字段并不能完全解决路由问题,因此我们需要模型层对控制层提供一个统一的接口,任何控制层与模型层的沟通都通过这个统一接口来完成,只不过不同请求报文组装的内容不一样而已,而这个接口还有个重要职责就是解析报文里的路由信息,让请求能被正确的路由到对应的模型接口所处理。当然这个接口的返回值最好也是一个统一的报文格式,这样控制层解析模型层的返回数据也会便利的多了。
由上所述,我们发现第一阶段的前后端分离工作控制层应该归属于web前端,这么做更加合理,也更加容易实现,其实之后进化版的前后端分离方案,控制层也都是属于web前端,只不过形式不同而已,这个我在下一篇文章里继续讨论。
第一阶段前后端分离方案解决的核心就是让控制层和模型层解耦,这个方案进一步演化一下,我们可以把控制层和视图层独立成一个web应用,模型层也独立成一个web应用,两个web应用之间通过远程调用方式进行沟通,这个方案我在以前文章里写过,这篇文章的名字叫做《我设计的网站的分布式架构》。
这个进化版的方案增加了系统开发的难度,因为我们需要增加网络通信的编程以及远程调用的实现,更麻烦的是我们还需要进行复杂的多线程编程,既然增加了开发的难度为什么我还要这么做呢?首先我们通过应用分层,可以动态的调节web前端和web服务端的负载压力,还可以在模型层之前提供一道安全屏障,不过被服务端绑架的web前端在提升整个web应用负载能力这块还是很有限的,其实这种做法的最大好处就是利于SOA框架的设计,也就是说这种架构我们可以为服务端的SOA化提供有力的保障,因为控制层和模型层的解耦,可以让模型层真正做到专注于业务,而不会再发生那种把业务逻辑写到控制层的问题了从而降低代码的健壮性。
好了,今天就写到这里,最好祝大家新年快乐,晚安了。