全球最快的JS模板引擎:tppl

废话很少说,先上测试:javascript

亲测请访问:【在线测试地址】单次结果不必定准确,请多测几回。html

tppl 的编译渲染速度是著名的 jQuery 做者 John Resig 开发的 tmpl 的 43 倍!与第二名 artTemplate 也有一倍的差距。 前端

彷佛每个大公司都选择本身开发模板引擎并将其开源,结果就是社区充斥着数不清的引擎,让人眼花缭乱无从选择。随着时间的流逝,愈来愈多的功能被添加进去,最终让一个强悍的发动机变成了一台臃肿复杂零件生锈的拖拉机。天呐,我就想网页面里插一段 html,你竟然要我往每一个js文件里再塞进500行代码!java

不,事情本来应该更简单。保持代码的简洁高效也意味着让生活更加健康愉悦。git

好吧,满脑子装着“封装”或者“模块化”的读者估计有不一样的见解。github

下面咱们来谈谈如何让引擎更增强劲高效。正则表达式

模板引擎分为两大主要阵营:数组

    1、原生语法浏览器

    2、自定义模板语法缓存

此两种方案各有各的好,自定义语法相比前者的优势在于,看起来和写起来更加规范简洁,更“像”是一种模板。也能更好的配合编译,而且能够避免用户写出“性能不佳”的代码。部分人认为自定义语法对页面设计人员来讲更为友好,这就见仁见智了。而缺点就是一般只能自定义 if else 和 for 循环等简单而有限的逻辑结构,不够强大和灵活。

自定义语法的优化方法有随着语法的不一样而不一样,但一般最终都是将其转换为原生的语言逻辑结构。这里主要讨论原生语法模板引擎的优化。

对于追求性能的模板引擎来讲,有两个显而易见的方向:

    1、编译结果缓存

    2、编译结果静态化

缓存很好理解,一次编译屡次渲染。一般是保存初步正则替换后的字符串中间值,重复渲染时直接拿来使用。

静态指的是,编译模板字符串生成一个字符串拼接的函数,而不是每次建立函数。渲染操做就至关于一次函数调用,代入变量完成字符串拼接并返回。通常引擎优化到这一步时,渲染方面已经没有太大的差距和进步的空间。你们都成了字符串拼接函数,只能在微小的语法层面作优化。这里说一下,相信从事过前端开发的朋友,都“据说”过一个字符串拼接的“快速方法”:将字符串片断 push 进一个数组,最后再 join 返回,性能比直接采用 + 或者 += 字符串要好。注意,这种方法过期了!在现代浏览器以及Node.js中已经再也不成立。经过数组链接字符串只是一个“临时解决方案”,随着各大js编译器的优化和进步,直接采用 + 字符串操做,给了一个编译器在语言底层作出优化的机会,你们仍是着眼于将来吧。

渲染差距不大时,编译则还存在不少“水分”能够挤出来。固然,一部分模板引擎集成了诸如文件加载、Ajax等高级功能,对其性能方面有要求过高彷佛也不太合理。

通常原生语法模板引擎,都采用相似下面的字符串表示:

<h1> <%=title %> </h1>
<% for(var i in content){ %>
  <p>第<%=i%>段:<%=content[i]%></p>
<%}%>

而编译操做就是将这一段字符串转换成相似下面的函数:

function(data){
    var str = "<h1>  "+data.title+"  </h1>";
    for(var i in data.content){
        str += "<p>第"+i+"段:"+data.content[i]+"</p>";
    }
    return str;
}

一般状况都是改变字符串结构,去掉模板标签,再使用 new Function() 建立函数。

传统传递参数的实现经过遍历数据对象,把对象的名值分离,而后分别把对象成员名称做为new Function的参数名(即变量名),而后使用函数的appley调用方式传给那些参数。

tmpl 则使用了javascript不经常使用的 with 语句实现。 实现方式很简洁,省去了var这个关键字。tmpl 性能问题就出在 with 上面。javascript 提供的 with 语句,本意是想用来更快捷的访问对象的属性。不幸的是,with语句在语言中的存在,就严重影响了 javascript 引擎的速度,由于它阻止了变量名的词法做用域绑定。

而转换的方法有不少种,一部分采用 split() 截断分析,一部分采用全正则替换,更有强悍的引擎使用词法分析,总之各显神通。 

全正则替换的方案只是一长串的 .replace() 链式调用,看起来代码更加美妙,但因为存在中间过渡状态和方法而致使性能不佳。词法分析更没必要说,大量的正则拖慢编译速度。编译优化的重点就在,尽可能减小中间态,并减小复杂正则表达式的使用。通过实测,split() 截断分析能减小一部分正则,性能更好。

tppl 一开始使用“模板尾标签分割”,即:str.split("%>") 的方式,这与 tmpl 的实现方式不谋而合,上面的字符串被分割为6段,而后为每一段使用一次正则替换:

var tpls = ["<h1> <%=title", " </h1>","<% for(var i in content){ ", "<p>第<%=i", "段:<%=content[i]", "</p><%}"];

在后来的性能测试中,发现这种实现方式相比其它引擎,并无太大的提高。看来只能另辟蹊径了。

通过长时间的左思右想,终于发现采用“模板头标签分割”的方式,能大大减小分割结果的数量,可是须要修改模板标签:

<h1> [=:title:] </h1>
[: for(var i in content){ :]
  <p>第[=:i:]段:[=:content[i]:]</p>
[:}:]

方括号 [ 与冒号 : 组成的模板标签相比 <% 能更加清晰的区分html代码与js逻辑代码。经过 .split("[:") 将模板分割为3段:

var tpls = ["<h1> [=:title:] </h1>", " for(var i in content){ :]<p>第[=:i:]段:[=:content[i]:]</p>", "}:]"];

如此一来正则替换从6次降低到3次,性能提高将近一倍!并且随着代码逻辑结构的不一样,性能提高将会更大。

关键的正则表达式:

var line = "'"+"<p>第[=:i:]段:[=:content[i]:]</p>".replace(/\[\=\:(.*?)\:\]/g, "'+$1+'")+"'";
// '<p>第'+i+'段:'+content[i]+'</p>'

tppl 的源码托管在 Github 上,地址:https://github.com/yangjiePro/tppl

若是你还有更好编译优化方法,欢迎讨论!

相关文章
相关标签/搜索