基于MVC模式的web框架在渲染页面时,都会提供可以内嵌后端语言的模板引擎,用于使用动态数据生成页面。在某些场景下,无法使用后端的模板引擎,但又需要使用动态数据渲染页面内容,这时便可选择基于Javascript的模板引擎。
背景:之前公司有一套C/S产品线,后来为了产品的web化,在中间件上封装了一个Restful service接口,用以响应web前端的数据请求。前端只提供了供Javascript调用的数据接口,返回的数据格式为序列化的JSON。因此页面的渲染只能在前端,由Javascript调用Restful service接口获得动态数据之后才能进行。
基于这种场景,考虑寻找一个Javascript的模板引擎进行页面渲染。由于本次需求(一个网站)相对简单,因此没有选择功能强大且复杂的模板引擎,而是采用了一个极其简单的引擎。
这个模板引擎是John Resig在几年前写的(这里),代码非常简洁。jquery之前有几个版本提供了模板引擎的功能,后来又去掉了,我没有实际用过jquery的模板功能,只搂了一眼使用方式,猜测跟这个模板引擎应该是有渊源的。John在他的书《 Secrets of the JavaScript Ninja》里也对这段代码做了介绍。下面是这个模板引擎的全部代码:
/*
* javascript template from John Resig
* @see : http://ejohn.org/blog/javascript-micro-templating
*/
(function () {
var cache = {};
this.tmpl = function tmpl (str, data) {
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
new Function ("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
"with(obj){p.push('" +
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
return data ? fn(data) : fn;
};
})();
代码只提供了一个tmpl()函数,函数内部进行的是字符串操作,把data填入模板然后生成一段html字符串。
一个模板示例:
<script type="text/template" id="actsListTmpl">
<div class="contContainer">
<h2 class="h2_bg">活动</h2>
<ul id="allActs" class="arrowlist f14">
<% if (acts.length === 0) { %>
<li>暂无活动</li>
<% } %>
<% for ( var i = 0; i < acts.length; i++ ) { %>
<li>
<span class="rightText">
<div class="jobOvsiteTruncate" title="<%=acts[i].site%>">
<%=acts[i].site%>
</div>
</span>
<a name="<%=acts[i].ID%>" href="#">
<div class="jobOvnameTruncate" title="<%=acts[i].ActiName%>">
<%=acts[i].ActiName%>
</div>
</a>
</li>
<% } %>
</ul>
</div>
</script>
模板被写在一个<script>标签里面,注意<script>标签的type="text/template",这并不是标准的content-type。当浏览器检测到内嵌<script>的type为不能识别的content-type时,会忽略掉其中的内容,因此这段代码既不会被页面渲染也不会被当做脚本来执行。这提供了我们一种在页面中巧妙地内嵌轻量级模板的方法。
模板中内容的写法,类似MVC框架中模板引擎的写法,采用<%= %>引入变量,使用<%%>引入Javascript的分支、循环等逻辑,这已经满足了基本的模板引擎应用。
使用上面的模板生成html内容:
var actsData = {
acts: [
{ID: "xxx", ActiName: "xxxxxx", site: "xxxx"},
{ID: "xxx", ActiName: "xxxxxx", site: "xxxx"},
{ID: "xxx", ActiName: "xxxxxx", site: "xxxx"}
]
};
tmpl("actsListTmpl", actsData);
其中"actsListTmpl"是为模板的<script>标签指定的ID,actsData是构造的代入模板的数据对象。tmpl方法会返回一串html代码,我们只需将这段html插入页面想要的位置就可以了:
<div id="actsList"></div>
var acts = document.getElementById("actsList");
acts.innerHTML = tmpl("actsListTmpl", actsData);
如上,我们可以把模板预置在页面中,使用ajax方式获取动态数据,然后使用tmpl方法渲染出最终的页面。
项目中遇到的一个问题是,有个模板需要在不同的页面上使用。因此便把这个模板抽离出来,单独写在了一个html文件中,在需要使用这个模板的页面上只写了一个script标签,准备在加载页面时把模板内容动态加载到script标签中:
模板:
<table class="ListTable">
<thead>
<tr>
<th class="CollectColumn"></th>
<th class="NameColumn">名称</th>
<th class="SiteColumn">地点</th>
<th>类别</th>
</tr>
</thead>
// 省略
页面标签:
<script type="text/template" id="fullListTmpl"></script>
然后加载页面时,把模板内容加载到script标签中:
$("#fullListTmpl").load("listTemplate.html", function () {
// callback
});
这时遇到的一个问题是: IE8中不允许向script标签appendChild,因此不得已在IE8时,使用了一个隐藏的div元素作为模板的容器:
<!--[if lte IE 8]>
<div style="display: none;" id="fullListTmpl"></div>
<![endif]--><!-- cann't append child to <script> tag in IE8 -->
<script type="text/template" id="fullListTmpl"></script>
因为时间原因没有做过多考虑,这里也许会有更好的解决方法。
需要注意的是,这段代码实现的是最简单的模板引擎功能,适用于只需要简单的页面渲染功能的应用场景,对于一些较复杂的页面可能并不适用。若需要实现较复杂的前端渲染,应该考虑功能更完善的模板引擎或一些前端MVVM框架。这段模板引擎非常精巧,代码也很简洁,在实际使用的时候可以根据需求对其进行修改,以满足需要。以这段代码为基础,可以扩展出一个功能非常强大的Javascript模板引擎。如果你打算在项目中使用这个小巧的模板引擎,请做充分的全局考虑,看它是否能够满足项目的需求。
来源:oschina
链接:https://my.oschina.net/u/187340/blog/214049