首页 > 代码库 > 模版+数据分离渲染方式的设计与实现

模版+数据分离渲染方式的设计与实现

一 背景
1 现状
  • 模版存放于后端
  • php输出页面html结构进行页面渲染
  • ajax请求,需要重渲结构时,php输出html结构
  • builder制作静态页面结构
  • jser完成页面交互逻辑开发
2 不足
  • 模版数据无法存储本地,导致每次打开页面请求数据量巨大
  • 数据每次要从接入层web服务器读取,没有合理利用CDN加速静态模版内容
  • 联调成本较大,不利于前端控制页面展示和交互开发
3 解决方案
  • 后端直接输出json数据
  • 试图把渲染页面的模版存放在前端
4 技术路线
 
技术分享
5 理论意义
  • 利用CDN保存html模版,看起来第一次请求会使用多一点的资源,实际上消耗并不是很大,且多次请求可以减小下载量
  • 充分利用缓存,提高性能
  • 联调成本大大降低,后端只需要按规则输出Json数据,前端能更大程度的控制页面展示,减小bug量
二 基于模版与数据分离渲染方式的方案设计
1 ejs模版简介
        官方API地址:http://www.embeddedjs.com/
        官方API介绍的很清楚,也是大多数模版的常用办法。下面列举几个特殊用法:
  • 可自定义函数
技术分享
         支持常用html标签的快捷方式,就像ruby on rails framework的做法一样,让代码更简洁。
  • 支持模版错误提示

技术分享

  • 另外,ejs支持nodejs
2 pageletView渲染组件简介
  • pageletView按照页面的模块分别输出模块id,css,js,html
技术分享
3 ejs与pageletView的融合方案设计
        pageView在渲染页面的时候,是分模块进行渲染的。这种方式是实现bigpipe的重要渲染方式,那么需要我们在不改变原有pageview的基础上进行改造。
即在原有的技术上,多传给STK.pageletM.view方法参数的两个属性,一个属性命名为data,用来存放json数据传递给前端使用,另一个属性命名为template,用来存放模块的模版文件。

技术分享

然而,由于页面上有多少个模块,就会有多少个template文件,在实现完成这种渲染之后,发现如果一个页面上的模块比较多的话,就会给页面渲染带来一定的负担,所以决定把一个页面的模版合并成一个模版文件,这样只需要请求一次模版文件就可以了。设计如下:

技术分享

三 基于模版与数据分离渲染方式的实现
1模版的传递办法
        ejs拿到模版和数据后,执行渲染。数据可以在pageLet拿到数据的时候,通过参数传递,而每个模块的渲染,需要动态的传递模块template,不 可能直接$Import写死到ejs文件,也不可能$Import写死到pageletView文件里。这个问题有两种办法解决。
  • 把模版设置成全局变量
  • 使用listener广播事件监听

显然,第一种把模版设置成全局变量的办法有很大风险,容易造成混淆和错乱,切不利于维护。于是我们选择第二种办法,listener事件广播的形式。因为 第二种办法,在需要使用到模版的地方,我们通过架设管道的方式,一对一的监听模版发出的广播,从ejs模版里进行接收,这样就避免了抛到全局造成的问题。

 
2 实现无等待的模版渲染
        然而,在使用listener事件的时候又遇到了问题。
        因为熟悉listener事件的同学都知道,事件fire与事件register是有先后顺序的,register在前,fire在后,也就是说在ejs 需要使用模版的时候,我们会使用listener的register方法绑定事件,然后才可以把模版fire出来,那么我们很容易想到把合并完的模版文件 a.js放在pageletView渲染模块之后,但是放在后面我们是无法监听到对应模块的pageletView已经register完成的,这时候如 果使用setTimeout的方式虽然能够解决问题但是存在着很大的弊端,因为第一,setTimeout需要耗费性能,渲染出页面的体验非常差,第二, 我们做了个测试,如果setTimeout的时间参数小于1000ms的话,成功渲染所有模块只能成为偶发的事情。
        怎么解决这个问题呢,我们最后想到了一个办法,使用两个广播进行解决。
        首先,我们把模版合并好的a.js文件放在pageletM(pageletM会调用pageletView方法)渲染之前,register一个广播,广播的名字叫做id+’_temp’,在这个广播里我们去把模版fire出去,fire的参数为id。
     技术分享
        其次,我们在ejs文件里register一个事件,这个参数为模块的id(即pid),然后fire一个事件,fire的参数为id+’_temp’
     技术分享
 
        整个传递的步骤为:
        首先ejs文件监听模版文件,然后模版文件fire出模版,整个目的就达到了。
        这个过程描述如图:
        说明:首先页面加载模版文件,模版文件监听pageletView文件reday的listener事件,回调函数fire出“模版”,然后页面运行 pageletView文件,pageletView文件注册了模版文件ready的事件,然后再pageView文件里发出模版pagelet已经 ready的信号。这样在pageletView执行的时候,模版告诉pageletView模版已经ready,监听过模板ready的回调函数就可以 收到模版从而完成模板与数据拼接了。这样就实现了无等待的数据加模版渲染。
     技术分享
 
3 pageletView中的向下兼容
         由于微博现如今使用的方式是模版放在后端进行,前端取到后端传出的html,然后进行渲染,若使用template+data的方式进行页面渲染,我们 可以做一下向下兼容。如下图,判断若data != undefined则通过template+data进行渲染,否则使用原有的方式进行渲染。
     技术分享
4 实现trans请求的模版渲染
        大家知道,前端对后端ajax请求成功的时候,若前端需要改变某个节点的innerHTML,后端会返回一段html代码,而当我们点击feed分页的时 候,可能会浪费大量的模版流量,如果把模版存在前端,后端只需要提供每个feed需要的数据就可以了,这个可以节省很多资源。
        实现这种方式很简单,只需要按照pageletView中的使用方式就可以了,但是这样的话会存在一个问题。前端拿到模版和数据拼接好html结构后,需 要知道怎么去处理这段html,所以我们把insertHTML集成在ejs中,通过传递参数的形式,去让ejs执行innerHTML或者是 insertHTML(如下图)。
     技术分享
 5 实现ejs文件的简单使用
        每次使用可能我们都需要注册一个事件,并在这个事件内抛出另一个事件,使用起来非常不方便。经过分析与设计,我们决定把模版中的注册事件和fire事件封装起来,调用的过程中只需要传递几个参数就可以实现效果。如下图:
 
        模版文件引用sedEjsTemplate.js,业务文件引用$.kit.dom.ejs文件(pageletView对$.kit.ejs的引用是一种特例)。
               技术分享
 
技术分享
 
6 template+data使用步骤(非常简单好用)
        第一步:写模版文件。
                    技术分享
 
        第二步:使用$.kit.dom.ejs();
          技术分享
        参数说明:技术分享

模版+数据分离渲染方式的设计与实现