`

<转载> javascript模版引擎-tmpl的bug修复与性能优化

 
阅读更多

一篇非常有用的关于js模板的文章.备份之.

原文地址:  http://yiheng.iteye.com/blog/1577360

javascript模版引擎-tmpl的bug修复与性能优化

 

在平时编码中,经常要做拼接字符串的工作,如把json数据用HTML展示出来,以往字符串拼接与逻辑混在在一起会让代码晦涩不堪,加大了多人协作与维护的成本。而采用前端模板机制就能很好的解决这个问题。

精妙的 tmpl

前端模板类开源的不少,但最属 jQuery 作者 John Resig 开发的 “javascript micro templating” 最为精妙,寥寥几笔便实现了模板引擎核心功能。

它的介绍与使用方式请看作者博客:http://ejohn.org/blog/javascript-micro-templating/

让我们先看看他的源码:

 

Javascript代码 
  1.  (function(){  
  2.   var cache = {};  
  3.   
  4.   this.tmpl =  function (str, data){  
  5.     var fn = !/\W/.test(str) ?  
  6.       cache[str] = cache[str] ||  
  7.         tmpl(document.getElementById(str).innerHTML) :  
  8.   
  9.       new Function("obj",  
  10.         "var p=[],print=function(){p.push.apply(p,arguments);};" +  
  11.         "with(obj){p.push('" +  
  12.   
  13.         str  
  14.           .replace(/[\r\t\n]/g, " ")  
  15.           .split("<%").join("\t")  
  16.           .replace(/((^|%>)[^\t]*)'/g, "$1\r")  
  17.           .replace(/\t=(.*?)%>/g, "',$1,'")  
  18.           .split("\t").join("');")  
  19.           .split("%>").join("p.push('")  
  20.           .split("\r").join("\\'")  
  21.       + "');}return p.join('');");  
  22.   
  23.     return data ? fn( data ) : fn;  
  24.   };  
  25. })();  

 

麻雀虽小,五脏俱全,除了基本的数据附加外,还拥有缓存机制、逻辑支持。现在,若要我评出一个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 的源码:

 

Javascript代码 
  1. //author:yaya,jihu  
  2. //uloveit.com.cn/template  
  3. //how to use?  YayaTemplate("xxx").render({});  
  4. var YayaTemplate = YayaTemplate || function(str){  
  5.     //核心分析方法  
  6.     var  _analyze=function(text){  
  7.         return text.replace(/{\$(\s|\S)*?\$}/g,function(s){   
  8.             return s.replace(/("|\\)/g,"\\$1")  
  9.                     .replace("{$",'_s.push("')  
  10.                     .replace("$}",'");')  
  11.                     .replace(/{\%([\s\S]*?)\%}/g, '",$1,"')  
  12.         }).replace(/\r|\n/g,"");  
  13.     };  
  14.     //中间代码  
  15.     var _temp = _analyze(document.getElementById(str)?document.getElementById(str).innerHTML:str);  
  16.     //返回生成器render方法  
  17.     return {  
  18.         render : function(mapping){  
  19.             var _a = [],_v = [],i;  
  20.             for (i in mapping){  
  21.                     _a.push(i);  
  22.                     _v.push(mapping[i]);  
  23.             }  
  24.             return (new Function(_a,"var _s=[];"+_temp+" return _s;")).apply(null,_v).join("");  
  25.         }  
  26.     }  
  27. };  

 

若把性能问题上升到一个“学术问题”的高度尝试去解决,为什么 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 优化版最终代码:

 

Javascript代码 
  1. /** 
  2.  * 微型模板引擎 tmpl 0.2 
  3.  * 
  4.  * 0.2 更新: 
  5.  * 1. 修复转义字符与id判断的BUG 
  6.  * 2. 放弃低效的 with 语句从而最高提升3.5倍的执行效率 
  7.  * 3. 使用随机内部变量防止与模板变量产生冲突 
  8.  * 
  9.  * @author  John Resig, Tang Bin 
  10.  * @see     http://ejohn.org/blog/javascript-micro-templating/ 
  11.  * @name    tmpl 
  12.  * @param   {String}    模板内容或者装有模板内容的元素ID 
  13.  * @param   {Object}    附加的数据 
  14.  * @return  {String}    解析好的模板 
  15.  * 
  16.  * @example 
  17.  * 方式一:在页面嵌入模板 
  18.  * <script type="text/tmpl" id="tmpl-demo"> 
  19.  * <ol title="<%=name%>"> 
  20.  *  <% for (var i = 0, l = list.length; i < length; i ++) { %> 
  21.  *      <li><%=list[i]%></li> 
  22.  *  <% } %> 
  23.  * </ol> 
  24.  * </script> 
  25.  * tmpl('tmpl-demo', {name: 'demo data', list: [202, 96, 133, 134]}) 
  26.  *  
  27.  * 方式二:直接传入模板: 
  28.  * var demoTmpl = 
  29.  * '<ol title="<%=name%>">' 
  30.  * + '<% for (var i = 0, l = list.length; i < length; i ++) { %>' 
  31.  * +    '<li><%=list[i]%></li>' 
  32.  * + '<% } %>' 
  33.  * +'</ol>'; 
  34.  * var render = tmpl(demoTmpl); 
  35.  * render({name: 'demo data', list: [202, 96, 133, 134]}); 
  36.  *  
  37.  * 这两种方式区别在于第一个会自动缓存编译好的模板, 
  38.  * 而第二种缓存交给外部对象控制,如例二中的 render 变量。 
  39.  */  
  40.   
  41. var tmpl = (function (cache, $) {  
  42. return function (str, data) {  
  43.     var fn = !/\s/.test(str)  
  44.     ? cache[str] = cache[str]  
  45.         || tmpl(document.getElementById(str).innerHTML)  
  46.   
  47.     : function (data) {  
  48.         var i, variable = [$], value = [[]];  
  49.         for (i in data) {  
  50.             variable.push(i);  
  51.             value.push(data[i]);  
  52.         };  
  53.         return (new Function(variable, fn.$))  
  54.         .apply(data, value).join("");  
  55.     };  
  56.   
  57.     fn.$ = fn.$ || $ + ".push('"   
  58.     + str.replace(/\\/g, "\\\\")  
  59.          .replace(/[\r\t\n]/g, " ")  
  60.          .split("<%").join("\t")  
  61.          .replace(/((^|%>)[^\t]*)'/g, "$1\r")  
  62.          .replace(/\t=(.*?)%>/g, "',$1,'")  
  63.          .split("\t").join("');")  
  64.          .split("%>").join($ + ".push('")  
  65.          .split("\r").join("\\'")  
  66.     + "');return " + $;  
  67.   
  68.     return data ? fn(data) : fn;  
  69. }})({}, '$' + (+ 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 作者的付出,正因为此我才有机会深入分析实现机制,解决问题并从中受益。独乐不如众乐,分享之。

分享到:
评论

相关推荐

    智能家居_物联网_环境监控_多功能应用系统_1741777957.zip

    人脸识别项目实战

    PLC热反应炉仿真程序和报告 ,PLC; 热反应炉; 仿真程序; 报告,PLC热反应炉仿真程序报告

    PLC热反应炉仿真程序和报告 ,PLC; 热反应炉; 仿真程序; 报告,PLC热反应炉仿真程序报告

    C++函数全解析:从基础入门到高级特性的编程指南

    内容概要:本文详细介绍了 C++ 函数的基础概念及其实战技巧。内容涵盖了函数的基本结构(定义、声明、调用)、多种参数传递方式(值传递、引用传递、指针传递),各类函数类型(无参无返、有参无返、无参有返、有参有返),以及高级特性(函数重载、函数模板、递归函数)。此外,通过实际案例展示了函数的应用,如统计数组元素频次和实现冒泡排序算法。最后,总结了C++函数的重要性及未来的拓展方向。 适合人群:有一定编程基础的程序员,特别是想要深入了解C++编程特性的开发人员。 使用场景及目标:① 学习C++中函数的定义与调用,掌握参数传递方式;② 掌握不同类型的C++函数及其应用场景;③ 深入理解函数重载、函数模板和递归函数的高级特性;④ 提升实际编程能力,通过实例强化所学知识。 其他说明:文章以循序渐进的方式讲解C++函数的相关知识点,并提供了实际编码练习帮助理解。阅读过程中应当边思考边实践,动手实验有助于更好地吸收知识点。

    `计算机视觉_Python_PyQt5_Opencv_综合图像处理与识别跟踪系统`.zip

    人脸识别项目实战

    Ultra Ethernet Consortium规范介绍与高性能AI网络优化

    内容概要:本文主要介绍了Ultra Ethernet Consortium(UEC)提出的下一代超高性能计算(HPC)和人工智能(AI)网络解决方案及其关键技术创新。文中指出,现代AI应用如大型语言模型(GPT系列)以及HPC对集群性能提出了更高需求。为了满足这一挑战,未来基于超乙太网络的新规格将采用包喷射传输、灵活数据报排序和改进型流量控制等机制来提高尾部延迟性能和整个通信系统的稳定度。同时UEC也在研究支持高效远程直接内存访问的新一代协议,确保能更好地利用现成以太网硬件设施的同时还增强了安全性。 适合人群:网络架构师、数据中心管理员、高性能运算从业人员及相关科研人员。 使用场景及目标:①为构建高效能的深度学习模型训练平台提供理论指导和技术路线;②帮助企业选择最合适的网络技术和优化现有IT基础设施;③推动整个行业内关于大规模分布式系统网络层面上的设计创新。 阅读建议:本文档重点在于展示UEC如何解决目前RDMA/RoCE所面临的问题并提出了一套全新的设计理念用于未来AI和HPC环境下的通信效率提升。在阅读时需要注意理解作者对于当前网络瓶颈分析背后的原因以及新设计方案所能带来的具体好处

    (参考GUI)MATLAB道路桥梁裂缝检测.zip

    (参考GUI)MATLAB道路桥梁裂缝检测.zip

    pygeos-0.14.0-cp311-cp311-win-amd64.whl

    pygeos-0.14.0-cp311-cp311-win_amd64.whl

    微信小程序_人脸识别_克隆安装_社交娱乐用途_1741777709.zip

    人脸识别项目实战

    基于Matlab的模拟光子晶体光纤中的电磁波传播特性 对模式场的分布和有效折射率的计算 模型使用有限差分时域(FDTD)方法来求解光波在PCF中的传播模式 定义物理参数、光纤材料参数、光波参数、PC

    基于Matlab的模拟光子晶体光纤中的电磁波传播特性 对模式场的分布和有效折射率的计算 模型使用有限差分时域(FDTD)方法来求解光波在PCF中的传播模式 定义物理参数、光纤材料参数、光波参数、PCF参数及几何结构等参数 有限差分时域(FDTD)方法:这是一种数值模拟方法,用于求解麦克斯韦方程,模拟电磁波在不同介质中的传播 特征值问题求解:使用eigs函数求解矩阵的特征值问题,以确定光波的传播模式和有效折射率 模式场分布的可视化:通过绘制模式场的分布图,直观地展示光波在PCF中的传播特性 程序已调通,可直接运行 ,基于Matlab模拟; 光子晶体光纤; 电磁波传播特性; 模式场分布; 有效折射率计算; 有限差分时域(FDTD)方法; 物理参数定义; 几何结构参数; 特征值问题求解; 程序运行。,基于Matlab的PCF电磁波传播模拟与特性分析

    知识图谱与大模型融合实践研究报告:技术路径、挑战及行业应用实例分析

    内容概要:《知识图谱与大模型融合实践研究报告》详细探讨了知识图谱和大模型在企业级落地应用的现状、面临的挑战及融合发展的潜力。首先,介绍了知识图谱与大模型的基本概念和发展历史,并对比分析了两者的优点和缺点,随后重点讨论了两者结合的可行性和带来的具体收益。接下来,报告详细讲解了两者融合的技术路径、关键技术及系统评估方法,并通过多个行业实践案例展示了融合的实际成效。最后提出了对未来的展望及相应的政策建议。 适合人群:对人工智能技术和其应用有兴趣的企业技术人员、研究人员及政策制定者。 使用场景及目标:①帮助企业理解知识图谱与大模型融合的关键技术和实际应用场景;②指导企业在实际应用中解决技术难题,优化系统性能;③推动相关领域技术的进步和发展,为政府决策提供理论依据。 其他说明:报告不仅强调了技术和应用场景的重要性,还关注了安全性和法律法规方面的要求,鼓励各界积极参与到这项新兴技术的研究和开发当中。

    (参考GUI)MATLAB BP神经网络的火焰识别.zip

    神经网络火焰识别,神经网络火焰识别,神经网络火焰识别,神经网络火焰识别,神经网络火焰识别

    人脸识别_实时_ArcFace_多路识别技术_JavaScr_1741771263.zip

    人脸识别项目实战

    telepathy-farstream-0.6.0-5.el7.x64-86.rpm.tar.gz

    1、文件内容:telepathy-farstream-0.6.0-5.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/telepathy-farstream-0.6.0-5.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊

    基于Springboot框架的购物推荐网站的设计与实现(Java项目编程实战+完整源码+毕设文档+sql文件+学习练手好项目).zip

    本东大每日推购物推荐网站管理员和用户两个角色。管理员功能有,个人中心,用户管理,商品类型管理,商品信息管理,商品销售排行榜管理,系统管理,订单管理。 用户功能有,个人中心,查看商品,查看购物资讯,购买商品,查看订单,我的收藏,商品评论。因而具有一定的实用性。 本站是一个B/S模式系统,采用Spring Boot框架作为开发技术,MYSQL数据库设计开发,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得东大每日推购物推荐网站管理工作系统化、规范化。 关键词:东大每日推购物推荐网站;Spring Boot框架;MYSQL数据库 东大每日推购物推荐网站的设计与实现 1 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 3 2.1 MYSQL数据库 3 2.2 B/S结构 3 2.3 Spring Boot框架简介 4 3系统分析 4 3.1可行性分析 4 3.1.1技术可行性 5 3.1.2经济可行性 5 3.1.3操作可行性 5 3.2系统性能分析 5 3.2.1 系统安全性 5 3.2.2 数据完整性 6 3.3系统界面

    使用C语言编程设计实现的平衡二叉树的源代码

    二叉树实现。平衡二叉树(Balanced Binary Tree)是一种特殊的二叉树,其特点是树的高度(depth)保持在一个相对较小的范围内,以确保在进行插入、删除和查找等操作时能够在对数时间内完成。平衡二叉树的主要目的是提高二叉树的操作效率,避免由于不平衡而导致的最坏情况(例如,形成链表的情况)。本资源是使用C语言编程设计实现的平衡二叉树的源代码。

    基于扩张状态观测器eso扰动补偿和权重因子调节的电流预测控制,相比传统方法,增加了参数鲁棒性 降低电流脉动,和误差 基于扩张状态观测器eso补偿的三矢量模型预测控制 ,基于扩张状态观测器; 扰动补

    基于扩张状态观测器eso扰动补偿和权重因子调节的电流预测控制,相比传统方法,增加了参数鲁棒性 降低电流脉动,和误差 基于扩张状态观测器eso补偿的三矢量模型预测控制 ,基于扩张状态观测器; 扰动补偿; 权重因子调节; 电流预测控制; 参数鲁棒性; 电流脉动降低; 误差降低; 三矢量模型预测控制,基于鲁棒性增强和扰动补偿的电流预测控制方法

    永磁同步电机全速域控制高频方波注入法、滑模观测器法SMO、加权切矢量控制Simulink仿真模型 低速域采用高频方波注入法HF,高速域采用滑膜观测器法SMO,期间采用加权形式切 送前方法 1、零低速

    永磁同步电机全速域控制高频方波注入法、滑模观测器法SMO、加权切矢量控制Simulink仿真模型 低速域采用高频方波注入法HF,高速域采用滑膜观测器法SMO,期间采用加权形式切 送前方法 1、零低速域,来用无数字滤波器高频方波注入法, 2.中高速域采用改进的SMO滑模观测器,来用的是sigmoid函数,PLL锁相环 3、转速过渡区域采用加权切法 该仿真各个部分清晰分明,仿真波形效果良好内附详细控制方法资料lunwen 带有参考文献和说明文档,仿真模型 ,核心关键词: 1. 永磁同步电机; 2. 全速域控制; 3. 高频方波注入法; 4. 滑模观测器法SMO; 5. 加权切换矢量控制; 6. Simulink仿真模型; 7. 零低速域控制; 8. 中高速域控制; 9. 转速过渡区域控制; 10. 仿真波形效果; 11. 详细控制方法资料; 12. 参考文献和说明文档。,永磁同步电机多域控制策略的仿真研究

    Buck变器二阶LADRC线性自抗扰控制matlab仿真 包括电压电流双闭环和ladrc控制外环加电流内环控制两种 并进行了对比,ladrc控制超调更小,追踪更快 参考文献 版本为2018b

    Buck变器二阶LADRC线性自抗扰控制matlab仿真 包括电压电流双闭环和ladrc控制外环加电流内环控制两种 并进行了对比,ladrc控制超调更小,追踪更快 参考文献 版本为2018b ,关键词:Buck变换器;二阶LADRC;线性自抗扰控制;Matlab仿真;电压电流双闭环;LADRC控制外环;电流内环控制;对比;超调;追踪;2018b版本。,Matlab仿真二阶LADRC控制的Buck变换器:外环LADRC+内环电流控制对比

    2024全球工程前沿.pdf

    2024全球工程前沿.pdf

Global site tag (gtag.js) - Google Analytics