一个精巧的Javascript Template引擎

£可爱£侵袭症+ 提交于 2019-12-10 16:43:08

基于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模板引擎。如果你打算在项目中使用这个小巧的模板引擎,请做充分的全局考虑,看它是否能够满足项目的需求。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!