前端的模板引擎跟后端模板引擎很相似,比如JSP或cshtml(razor),他们的语法都非常相似,他们所实现的功能也几乎一样:将数据绑定到HTML模板。VueJs和react都可以充当这样的模板引擎。我们最终没有选用react而是选用了VueJs的原因只有一个,那就是VueJs是真正的响应式,而react改变model之后需要手工调用setState才会更新UI,这是完全无法忍受的。
因为这个原因,我们只能选择VueJS作为模板引擎。
3、我们的前端框架的工作原理
虽然本文写的有点长,但我们的前端框架却是非常简单的,这也是我们为什么不选择采用.vue文件构建工程的原因,因为那太复杂了。
核心工作原理:
(1)定义页面URL格式。我们每个页面的地址格式大概长这样:#home/index。admin代表模块名称,#后面的home代表子模块对应controller,而index对应controller里面的方法。所以当在同一个模块中切换页面的时候,只会改变地址的hash值,浏览器不会进行跳转。但是当切换页面之后,如果用户点击浏览器的刷新按钮,框架能够根据hash值加载当前页面。
(2)根据URL加载layout.html。当服务器收到“/admin#home/index”地址的请求时,不管后面的hash值为多少,直接返回一个layout.html。该layout.html文件包含页面的基本框架,比如公共js、css页面、导航、footer等公共元素。
(3)初始化layoutVue。Layout.html加载完成之后,其中的JS会根据layout.html的HTML结构生成一个Vue实例,并将layout.html下面的全部动态HTML交给该Vue实例托管,比如根据用户角色显示相应的导航菜单,在页面header显示用户个人信息等。
(4)根据location.hash通过jQuery ajax加载相应的内容HTML文件。这时我们会在服务器端定义另一个接口,根据内容页的路径返回对于的html代码。
比如:GET /admin/getPage?path=home/index。
内容页的html除了返回html代码之外,还会包含该页面所需的JS和CSS。这样,当内容页的html呈现到layout中的某个容器div中后,内容页的JS就会被加载并执行。那么内容页的JS都有些什么逻辑呢?当然是初始化内容页的Vue实例并接管内容页的动态html生成工作。
以上4个步骤可以用下图简单表示:
读到这里,如果你对软件架构很有经验,那么相信你已经完全明白了我们的前后端分离框架的工作原理了,你也应该可以按照本文的思路完成你自己的前后端分离框架了。但是对于大多数读者来说,可能读到这里只是大概明白怎么回事,如果说要自己动手开始搭建,可能就会面临无从下手的尴尬了。不用担心,接下来笔者就以我们的框架为例,一步一步通过代码来展示我们框架的搭建过程。
4、一步一步搭建我们的前端框架
(1)页面地址生成。服务器根据同步请求URL(/pe#home/index…)响应一个layout.html. 请看java源码:
上图中的java代码就是我们前端框架中全部的后端处理代码了(请仔细读这句话,有点绕哈),是不是非常简单呢?虽然简单,但是功能却是很强大的。从上面的代码中可以看出:
a. 只要是以“/pe#”开头的URL,服务器将直接返回某个目录下的layout.html页面;
b. 前端开发人员可以在“/modules/pe/views/”下面随便建立目录、子目录以及HTML文件,然后即可通过ajax请求类似“/pe/page?path=home/index”这样的URL直接加载下面的HTML文件,这样前端开发人员不需要动一行后端代码,只需要按照约定建立目录和HTML文件就可以在浏览器中加载出来。这样,前端开发人员就完全不需要依赖后端开发人员来获得页面地址了,前端开发人员自己就可以创建页面地址!
上面的java代码中,还可以将同步请求跟异步请求合并成一个(@GetMapping(“/pe/*”)),然后在方法内部判断是否是同步请求,如果是同步请求就返回layout.html,否则返回内容页。这样做就不是监听hash变化了,而是每个URL地址都看上去像一个同步请求地址,如“/pe/home/index”的URL进入服务器后,服务器首先返回layout.html,然后layout.html再发起ajax请求“/pe/home/index”这时服务器返回内容页HTML。在我们项目中,我们更愿意使用监听hash变化的方式。当然笔者认为这两种方式都是OK的,如果后期想切换也是比较容易的。
(2)layout.html。 下面让我们来看看服务器首先返回的layout.html大概长什么样子,请看下图: