在平时编码中,经常要做拼接字符串的工作,如把json数据用HTML展示出来,以往字符串拼接与逻辑混在在一起会让代码晦涩不堪,加大了多人协作与维护的成本。而采用前端模板机制就能很好的解决这个问题。
精妙的 tmpl
前端模板类开源的不少,但最属 jQuery 作者 John Resig 开发的 “javascript micro templating” 最为精妙,寥寥几笔便实现了模板引擎核心功能。
它的介绍与使用方式请看作者博客:http://ejohn.org/blog/javascript-micro-templating/
让我们先看看他的源码:
- (function(){
- var cache = {};
- this.tmpl = function (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;
- };
- })();
麻雀虽小,五脏俱全,除了基本的数据附加外,还拥有缓存机制、逻辑支持。现在,若要我评出一个javascript 最节能的自定义函数排名,第一名是 $ 函数(document.getElementById 简版),而第二名就是 tmpl 了。
当然,它并非完美,我使用过程中发现了一些问题:
tmpl 美中不足
一、无法正确处理转义字符,如:
tmpl(
'<%=name%>\\<%=id%> '
, {name:
'糖饼'
, id:
'1987'
});
它就会报错。若正常工作,它应该输出:糖饼\1987
实际上解决起来很简单,添加一行正则对转义符进行转义:
str.replace(/\\/g,
"\\\\"
)
二、它有时候无法正确区分第一个参数是ID还是模板。
假若页面模板ID带有下划线,如 tmpl-photo-thumb 它不会去查找这个名称的模板,会认为这传入的是原始模板直接编译输出。
原始模板与元素id最直观的区别就是是否含有空格,因此改动下正则表达式即可:
!/\s/.test(str)
三、它内部还残有一处测试用的代码,可删除。
print=
function
(){p.push.apply(p,arguments);}
tmpl 效率的疑惑
直到前段时间看了百度mux一篇介绍 YayaTemplate 的软文,原文作者对各大流行的模板引擎进行了效率测试,最终得出 YayaTemplate 是最快的一个。 虽然测试结果 tmpl 不敌 YayaTemplate ,但也让我打消了对性能的顾虑,实际应用中与传统的字符串拼接差不多。它们只有进行超大规模的解析才会有较大的性能差距。(超大规模?javascript本身就不适合干这事。若哪天程序员一次性给浏览器插入上千条列表数据而其慢无比的时候,不用怀疑:问题出在了这个程序员身上,他不会爱惜用户的浏览器。)
若说到引擎效率排名问题,我倒不觉得这是不能是衡量模板引擎的首要标准,模板语法也是重要的一环,这时候 YayaTemplate 的模板语法就显得晦涩多了,它为了节省几个正则表达式而在模板语法上耍了小聪明。
先展示 YayaTemplate 的源码:
- //author:yaya,jihu
- //uloveit.com.cn/template
- //how to use? YayaTemplate("xxx").render({});
- var YayaTemplate = YayaTemplate || function(str){
- //核心分析方法
- var _analyze=function(text){
- return text.replace(/{\$(\s|\S)*?\$}/g,function(s){
- return s.replace(/("|\\)/g,"\\$1")
- .replace("{$",'_s.push("')
- .replace("$}",'");')
- .replace(/{\%([\s\S]*?)\%}/g, '",$1,"')
- }).replace(/\r|\n/g,"");
- };
- //中间代码
- var _temp = _analyze(document.getElementById(str)?document.getElementById(str).innerHTML:str);
- //返回生成器render方法
- return {
- render : function(mapping){
- var _a = [],_v = [],i;
- for (i in mapping){
- _a.push(i);
- _v.push(mapping[i]);
- }
- return (new Function(_a,"var _s=[];"+_temp+" return _s;")).apply(null,_v).join("");
- }
- }
- };
若把性能问题上升到一个“学术问题”的高度尝试去解决,为什么 tmpl 会比 YayaTemplate 慢?
语法解析?虽然 YayaTemplate 使用了一个新颖的 javascript 包裹 html 的方式作为模板语法,但最终都需要用正则表达式解析成标准的 javascript 语法,这里正则的效率不会有太大的差异,并且双方都使用了缓存机制确保只对原始模板仅进行一次解析。
数据转换?模板引擎会把数据最终以变量的形式保存在闭包中,以好让模板获取到。这里先对比下一下双方的变量声明机制:
YayaTemplate 使用传统传递参数的形式实现。它通过遍历数据对象,把对象的名值分离,然后分别把对象成员名称作为new Function的参数名(即变量名),然后使用函数的appley调用方式传给那些参数。
tmpl 则使用了javascript不常用的 with 语句实现。 实现方式很简洁,省去了var这个关键字。
tmpl 性能问题就出在 with 上面。javascript 提供的 with 语句,本意是想用来更快捷的访问对象的属性。不幸的是,with语句在语言中的存在,就严重影响了 javascript 引擎的速度,因为它阻止了变量名的词法作用域绑定。
优化 tmpl
tmpl 若去掉 with 语句,而改用传统的传参性能立即大提升,经过实测在24万条数据下 firefox 能提高 5 倍,chrome 2.4 倍,opera 1.84倍,safari 2.1倍,IE6 1.1倍,IE9 1.35倍,最终与 YayaTemplate 不分上下。
测试地址:http://www.planeart.cn/demo/tmpl/tmpl.html
tmpl 优化版最终代码:
- /**
- * 微型模板引擎 tmpl 0.2
- *
- * 0.2 更新:
- * 1. 修复转义字符与id判断的BUG
- * 2. 放弃低效的 with 语句从而最高提升3.5倍的执行效率
- * 3. 使用随机内部变量防止与模板变量产生冲突
- *
- * @author John Resig, Tang Bin
- * @see http://ejohn.org/blog/javascript-micro-templating/
- * @name tmpl
- * @param {String} 模板内容或者装有模板内容的元素ID
- * @param {Object} 附加的数据
- * @return {String} 解析好的模板
- *
- * @example
- * 方式一:在页面嵌入模板
- * <script type="text/tmpl" id="tmpl-demo">
- * <ol title="<%=name%>">
- * <% for (var i = 0, l = list.length; i < length; i ++) { %>
- * <li><%=list[i]%></li>
- * <% } %>
- * </ol>
- * </script>
- * tmpl('tmpl-demo', {name: 'demo data', list: [202, 96, 133, 134]})
- *
- * 方式二:直接传入模板:
- * var demoTmpl =
- * '<ol title="<%=name%>">'
- * + '<% for (var i = 0, l = list.length; i < length; i ++) { %>'
- * + '<li><%=list[i]%></li>'
- * + '<% } %>'
- * +'</ol>';
- * var render = tmpl(demoTmpl);
- * render({name: 'demo data', list: [202, 96, 133, 134]});
- *
- * 这两种方式区别在于第一个会自动缓存编译好的模板,
- * 而第二种缓存交给外部对象控制,如例二中的 render 变量。
- */
- var tmpl = (function (cache, $) {
- return function (str, data) {
- var fn = !/\s/.test(str)
- ? cache[str] = cache[str]
- || tmpl(document.getElementById(str).innerHTML)
- : function (data) {
- var i, variable = [$], value = [[]];
- for (i in data) {
- variable.push(i);
- value.push(data[i]);
- };
- return (new Function(variable, fn.$))
- .apply(data, value).join("");
- };
- fn.$ = fn.$ || $ + ".push('"
- + str.replace(/\\/g, "\\\\")
- .replace(/[\r\t\n]/g, " ")
- .split("<%").join("\t")
- .replace(/((^|%>)[^\t]*)'/g, "$1\r")
- .replace(/\t=(.*?)%>/g, "',$1,'")
- .split("\t").join("');")
- .split("%>").join($ + ".push('")
- .split("\r").join("\\'")
- + "');return " + $;
- return data ? fn(data) : fn;
- }})({}, '$' + (+ new Date));
模板引擎依赖 Function 构造器实现,它与 eval 一样提供了使用文本访问 javascript 解析引擎的方法,这也会让性能显著的降低,但此时 javascript 中已别无他法。
使用 Function 构造器还会对参数名称有所限制,所以导致数据成员命名必须与 javascript 变量名规范保持一致,否则会报错。好在这个错误可以在运行的时候立马被发现,而不会成为一颗地雷。
tmpl 使用小窍门
一、缓存优化。
tmpl 默认对嵌入到页面中的模板进行了缓存优化(即第一个参数为ID的时候),它只会对模板进行一次分析。若原始模板是直接传入到 tmpl 第一个参数中,且需要多次使用的话,建议用公用变量缓存起来,需要解析数据的时候再使用,以获得相同的优化效果。如:
// 生成模板缓存 var render = tmpl(listTmpl); // 可多次调用模板 elem.innerHTML = render(data1); elem.innerHTML = render(data2); ...
二、避免未定义的变量引起系统崩溃。
若模板中定义了一个变量输出,而且传入数据却少了这个项目就会出现变量未定义的错误,从而引起整个程序的崩溃。如果无法确保数据完整性,仍然有方法可以对对其成员进行探测。原版中暗含变量保存了原始传入的数据,即 obj ;而在我的升级版本中则是关键字 this,如:
<% if (this.dataName !== undefined) { %> <%=dataName %> <% } %>
三、调试模板。
由于模板引擎是用文本的调用的 javascript 引擎,调试工具无法定位到出错的行。在 升级版本 中你可以用调试工具输出编译好的模板缓存。例如调试这个模板:
<script id="tmpl" type="text/tmpl"> <ul> <% for (var i = 0, l = list.length; i < l; i ++) { %> <li><%=list[i].index%>. 用户: <%=list[i].user%>; 网站:<%=list[i].site%></li> <% } %> </ul>
输出缓存:
window.console(tmpl(
'tmpl'
).$);
日志结果:
"$1318348744541.push(' <ul> '); for (var i = 0, l = list.length; i < l; i ++) { $1318348744541.push(' <li>',list[i].index,'. 用户: ',list[i].user,'; 网站:',list[i].site,'</li> '); } $1318348744541.push(' </ul> ');return $1318348744541"
现在你可以看到模板引擎编译好的javascript语句,可以对照这检查模板是否存在错误。($1318348744541是一个随机名称的临时数组,可忽略)
最后非常感谢 tmpl 原作者 与 YayaTemplate 作者的付出,正因为此我才有机会深入分析实现机制,解决问题并从中受益。独乐不如众乐,分享之。
相关推荐
### JavaScript模版引擎-tmpl的Bug修复与性能优化分析 #### 概述 在Web开发中,模版引擎是处理动态内容的一种常用技术。通过模版引擎,开发者可以轻松地将JSON数据转换为HTML格式,使得代码更加清晰、易于维护。`...
综上所述,文档通过分析javascriptmicrotemplating(tmpl)和YayaTemplate这两种模板引擎的实现、bug修复、性能测试与语法设计,全面介绍了模板引擎在前端开发中的应用及其优化方法。开发者可根据项目的实际需求和...
本文将深入探讨jQuery-Tmpl,一个用于在jQuery中实现模板引擎的插件,以及它如何帮助开发者构建动态、数据驱动的用户界面。 一、jQuery-Tmpl简介 jQuery-Tmpl是jQuery的一个扩展,提供了模板编译和渲染的功能。这...
【ape-tmpl】是一款专为前端开发者设计的开源库,它是APE框架的一部分,主要用于处理和渲染模板。在前端开发中,模板引擎是一个重要的工具,它能够帮助开发者将数据和视图逻辑分离,使得代码更加清晰,更易于维护。...
<template name="tmpl_15_container"> <template is="tmpl_0_#text" data="{{i:i}}" wx:if="{{i.nn==='#text'}}"></template> <comp i="{{i}}" l="{{l}}" wx:else></comp> </template> <template name="tmpl_14_...
2. **高效渲染**:`coz-tmpl`优化了模板的解析和渲染过程,确保在处理大量数据时仍能保持良好的性能。 3. **与`coz`无缝集成**:作为`coz`的一部分,`coz-tmpl`与其他`coz`工具如编译器、合并工具等协同工作,形成...
`jQuery tmpl`是一个轻量级的模板引擎,它使得JavaScript对象的数据绑定到HTML模板变得更加简单和直观,极大地提高了开发效率。 ### jQuery tmpl简介 jQuery tmpl是jQuery生态系统中的一个插件,它提供了一种简洁...
前端开源库-blueimp-tmplblueimp tmpl,1KB轻量级,快速和强大的javascript模板引擎,零依赖性。与node.js等服务器端环境、requirejs、browserify或webpack等模块加载程序以及所有Web浏览器兼容。
Vue.js 是一款流行的前端JavaScript框架,它的核心特性之一就是组件化。在组件化开发中,有时我们需要在组件内部自定义一些动态内容,这就引出了Vue的插槽(Slot)机制。插槽允许父组件向子组件传递内容,并决定这些...
《jQuery tmpl:强大的JavaScript模板引擎解析》 在前端开发中,数据绑定和动态渲染是常见的需求,jQuery tmpl作为一款强大的JavaScript模板引擎,为开发者提供了高效、简洁的解决方案。本文将深入探讨jQuery tmpl...
然而,有时候开发者会遇到一个常见的问题,即在HTML中找不到与Repeater控件对应的ID,导致tmpl模板无法正常地appendTo Repeater标签中。这个问题通常是由于对Repeater工作原理理解不透彻或者JavaScript操作不当所...
具有JavaScriptHTML或字符串模板引擎 特征 速度快,体积小且没有依赖性。 支持包括内部模板。 支持json,array和任何js对象作为渲染数据。 简洁的语法,易于使用。 支持所有常用的浏览器。 支持使用html脚本或...
tmpl模板插件是一款基于jQuery的高效、轻量级的模板引擎,它允许开发者将HTML结构与数据分离,方便地动态渲染页面内容。这个插件在Web开发中扮演着重要角色,尤其是在处理大量的异步数据更新时,能显著提高页面性能...
6. `<noscript>`标签:当浏览器不支持JavaScript或者JavaScript被禁用时,显示的替代内容。 ```html <noscript> Please enable JavaScript for full functionality. </noscript> ``` 7. `<base>`标签:为页面...
然而,“ukautz-tmpl”项目可能在其基础上进行了扩展和优化,提供了更丰富的功能和更好的性能,或者是为了满足特定的开发需求。 首先,我们来了解一下Go中的模板工作原理。模板是一种声明式编程,它定义了数据如何...
添加的版本:1.4.3 jQuery.tmpl(template [,data] [,options]) template用作模板的HTML标记或文本。 数据的数据呈现。这可以是任何JavaScript类型,包括Array或Object。 options用户定义的键/值对的可...
artTemplate 是新一代 javascript 模板引擎,它采用预编译方式让性能有了质的飞跃,并且充分利用 javascript 引擎特性,使得其性能无论在前端还是后端都有极其出色的表现。在 chrome 下渲染效率测试中分别是知名引擎...