使用JS模板引擎实现前后端分离

前言

在web应用开发的架构中,普遍都会使用MVC设计来分离业务逻辑和视图元素,最终在视图部分使用JSP/Velocity/freemarker等技术把动态内容转换为HTML静态页面给浏览器显示,如下图所示


使用JS模板引擎实现前后端分离
 

在这种开发模式下前端写好静态demo,后端翻译成JSP/Velocity/freemarker模版,这种模式的问题就不说了,前端需要依赖后端数据才能测试,如果让前端人员基于后端环境开发也很痛苦,配置安装使用都很麻烦。另外,后端也没法摆脱对展现的强关注,从而专心于业务逻辑层的开发。

 

之所以产生这些问题,我认为传统的MVC框架本质其实是一个服务端的MVC框架,虽然MVC设计模式里的V即View视图层是想把界面开发工作专业化,让界面设计人员能专心于界面开发,但是传统的MVC框架下的View层的本质却是一个不折不扣的服务端技术。

 

向前端演进

我认为要解决这个问题最主要就是把页面渲染工作从服务器分离出来,因为渲染这块工作,对于前端开发者的日常工作来说,佔了非常大的比例,也是最容易与后端开发纠结不清的地方。

回首过去前端技术发展的这几年, View 这个层面的工作,经过了许多次的变革,像是:

    1.   Form Submit 全页刷新 => AJAX局部刷新

    2.   服务端续染 + MVC => 客户端渲染 + MVC

    3.   传统换页跳转 => 单页面应用

可以观察到在这几年,大家都倾向将 渲染 这件事,从服务器端端移向了浏览器端。

而服务器端则专注于 服务化 ,提供数据接口。

本文

 

当渲染工作由服务端转向浏览器端后,就变成了下图这样的设计


使用JS模板引擎实现前后端分离

 

 

服务端的MVC并不用取消,只是view的工作由页面渲染改为提供数据接口,同时在前端引入MVC设计,由前端View负责把数据渲染成最终页面。

好了,差不多进入正题了(-_-!),其实本文并不是讲解前端MVC设计,而是讲前端MVC设计过程中一个重要的技术问题:如何在前端渲染页面?

不过在讲前端渲染技术之前先了解下前端渲染的好处和坏处,并不是所有情况都适合在前端渲染,大家要根据项目实际情况取舍

浏览器端渲染的好处

浏览器端渲染的好处,我们都很清楚,像是

    1.    摆脱业务逻辑与呈现逻辑在Java模版引擎中的耦合与混乱。

    2.    针对多终端应用,更容易以接口化的形式。在浏览器端搭配不同的模版,呈现不同的应用。

    3.    页面呈现本来就不仅是html,在前端的渲染可以更轻易的以组件化形式 (html + js + css)提供功能,使得前端组件不需依赖于服务端产生的html结构。

    4.    脱离对于后端开发、发佈流程的依赖。

    5.   方便联调。

浏览器端渲染造成的坏处

但是在享受好处的同时,我们同样的也面临了 浏览器端渲染 所带来的坏处,像是:

    1.    模版分离在不同的库。有的模版放在服务端 (JAVA),而有的放在浏览器端 (JS)。前后端模版语言不相通。

    2.    需要等待所有模版与组件在浏览器端载入完成后才能开始渲染,无法即开即看。

    3.   首次进入会有白屏等待渲染的时间,不利于用户体验

    4.    开发单页面应用时,前端Route与服务器端Route不匹配,处理起来很麻烦。

    5.    重要内容都在前端组装,不利于SEO

 

JavaScript模板引擎

OK,终于进入正题了((@﹏@)~),前端渲染目前最好的方法就是使用js模板引擎,

什么是javascript模板引擎?

javascript模板引擎可以用来帮助开发人员有效的组织和分离前端页面代码中的显示层和数据层两个部分。这里我们主要集中在在前端页面内容的展示方面。如果你接触过后端相关开发的话,基本的思路和目地是一致的。具体这里我们举个简单的例子:

如果你需要将一个来自后台的JSON数据对象转化成页面显示内容的话,通常使用如下方式(具体演示使用jQuery):

var siteinfo={sitename:‘terry li’, siteurl:‘gbin1.com’};

var userwrapper = $(‘<ul>’)

.append(‘<li>’ + siteinfo.sitename+ ‘</li>’)

.append(‘<li>’ + siteinfo.siteurl + ‘</li>’);

var html = userwrapper.html();

以上代码非常简单,我们将得到的siteinfo对象直接使用字符串拼接来转化成页面需要展示的html标签。这里我们使用一个ul元素来展示站点的全部相关信息。

从代码本身来讲,无可厚非,大部分人对于简单的页面Html生成基本都使用这种方法,而比较现实的状况是,很多的前端开发人员都习惯这些书写代码,因为思路简单并且直接,当然,包括我本人。对于代码层次和数据结构简答的项目来说,这样的写法非常易于理解。但是如果这个JSON对象属性内容丰富复杂,并且前台显示逻辑稍微复杂一点儿的话我相信,使用这种字符串连接的方式,将绝对会让阅读维护代码的同事抓狂

而javascript模板引擎恰恰就是为了帮助我们有效的组织数据及其展示内容而出现的。和其它的模板使用方式一样,你需要做如下两个事情:

1. 创建展示模板

2. 将数据解析到模板中

 

    例如上面字符串组装如果用js模板写的话是这样(演示使用underscore模板)

var siteinfo={sitename:‘terry li’, siteurl:‘gbin1.com’};

var userwrapper = ‘<ul><li>{{= sitename }}</li><li>{{ =siteurl }}</li><ul>’;

var render = _.template(userwrapper);

var html = render(siteinfo);

 

    是不是很清晰,特别当页面内容灰常丰富时你就知道这种方式有多好了。

 

模板引擎的选择

目前网上开源的模板引擎真是百花齐放,太多选择了,国内的话推荐百度的baiduTemplate,阿里巴巴的kissyTemlate,腾讯的artTemplate,国外推荐的就有Mustache,jade,Handlebars ,EJS,doT.js,underscore.js等。

我目前在项目中使用的是underscore.js,因为模板只是underscore的一个小功能而已,他还提供了很多其他实用的功能作为对jquery的补充,比较适合企业内网的应用。另外underscore模板使用的是JavaScript原生语法,对于初学上手快,如果熟悉JavaScript那其实也就几分钟学完了。

如果项目中的人员都已经熟悉和习惯了用js模板,那么这时推荐用支持简洁语法的模板引擎(其实大部分模板都是使用简洁语法),或者有些同时支持简洁语法和原生语法,例如artTemplate.js

例如下面演示artTemplate的简洁语法和原生语法

简洁语法

推荐使用,语法简单实用,利于读写。

{{if admin}}
    {{include 'admin_content'}}
    {{each list}}
        <div>{{$index}}. {{$value.user}}</div>
    {{/each}}
{{/if}}

原生语法

<%if (admin){%>
    <%include('admin_content')%>
    <%for (var i=0;i<list.length;i++) {%>
        <div><%=i%>. <%=list[i].user%></div>
    <%}%>
<%}%>

 

最佳实践

大部分模板语言都把{{ }}或者<% %>里面的内容当做模板语言进行渲染,一般也会有自定义的功能可以更改这个默认符号。另外由于<% %>符号正好和JSP冲突,所以在JSP中可以使用{{}}代替

 

案例演示

下面我用内网应用中最常见的分页表格演示具体在项目中的应用

 

1、首先我们需要确定页面中哪些部分为动态内容,例如下图打了红框的部分为表格中的动态内容,分别为表格内容和分页信息;


使用JS模板引擎实现前后端分离
 

 2、然后我们用JavaScript模板改写动态内容部分(演示使用underscore引擎),看下图


使用JS模板引擎实现前后端分离
 

l  使用<script type="underscore/table">标签表示里面的内容为动态模板

l  第一个模板for循环content数据列表,然后输出里面的值

 

l  第二个模板直接输出分页信息

 

 

 

3、接着开发分页查询数据接口,接口返回json格式的数据,数据如下图 


使用JS模板引擎实现前后端分离

 

 

4、最后编写查询按钮的js控制,调接口查询,然后在回调方法里面调用模板渲染,例如用jquery类似这样写

 

//jquery查询

$.getJSON(

    'http://localhost:8080/test/query.action',

    function(data,status){

        //获取模板

        $('script type="text/javascript"').each(function(){

           var source = $(this).html();//取模板内容

           var render = _.template(source);

           var html = render(data);//渲染

           $(this).parent().html(html);//把渲染结果加入页面

        })

    }

);

 

 


 

相关推荐