template engines

JavaScript performance comparison

Revision 7 of this test case created by

Preparation code

Desired output:

<h2>Recipe for Brownies</h2>
<ul class="glossy rounded">
	<li>120 ml <strong>butter</strong></li>
	<li>120 ml <strong>unsweetened cocoa</strong></li>
	<li>240 ml <strong>sugar</strong></li>
	<li>2 <strong>eggs</strong></li>
	<li>10 ml <strong>vanilla</strong></li>
	<li>120 ml <strong>flour</strong></li>
	<li>1/4 tsp <strong>salt</strong></li>
</ul>

<script>
// The data:
// ------------
var the_data = {
	title: 'Recipe for Brownies',
	list: [{
		liststyle: 'glossy rounded',
		foodstuff: [
			{amount:'120', unit:'ml',  ingredient:'butter'},
			{amount:'120', unit:'ml',  ingredient:'unsweetened cocoa'},
			{amount:'240', unit:'ml',  ingredient:'sugar'},
			{amount:'2',               ingredient:'eggs'},
			{amount:'10',  unit:'ml',  ingredient:'vanilla'},
			{amount:'120', unit:'ml',  ingredient:'flour'},
			{amount:'1/4', unit:'tsp', ingredient:'salt'}
		]
	}]
};
</script>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/0.4.2/lodash.min.js"></script>
<script>var lodash = _.noConflict();</script>
<script src="https://raw.github.com/janl/mustache.js/master/mustache.js"></script>
<script src="http://cdn.kendostatic.com/2011.2.804/js/kendo.all.min.js"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<script src="http://twitter.github.com/hogan.js/builds/2.0.0/hogan-2.0.0.js"></script>

<script>

// Template (identical to Kendo UI template syntax)
var _template = '<h2><#= data.title #></h2><# for (var i=0, l=data.list.length; i<l; i++) { var itemI = data.list[i]; #><ul class="<#= itemI.liststyle #>"><# for (var j=0, m=itemI.foodstuff.length; j<m; j++) { var itemJ = itemI.foodstuff[j]; #><li><#= itemJ.amount #><# if (itemJ.unit) { #> <#= itemJ.unit #><# } #> <strong><#= itemJ.ingredient #></strong></li><# } #></ul><# } #>';



// -------------------------------------------------
// Underscore.js Templates

// using custom template settings
_.templateSettings = {
  'evaluate': /<#([\s\S]+?)#>/g,
  'interpolate': /<#=([\s\S]+?)#>/g,
};
// Precompile
var underscore_func = _.template(_template, null, {variable: 'data'});



// -------------------------------------------------
// lodash.js Templates

// using custom template settings
lodash.templateSettings = {
  'evaluate': /<#([\s\S]+?)#>/g,
  'interpolate': /<#=([\s\S]+?)#>/g,
};
// Precompile
var lodash_func = lodash.template(_template, null, {variable: 'data'});



// -------------------------------------------------
// Mustache.js

// Template
var mustache_template = '<h2>{{title}}</h2>{{#list}}<ul class="{{liststyle}}">{{#foodstuff}}<li>{{amount}} {{#unit}}{{unit}} {{/unit}}<strong>{{ingredient}}</strong></li>{{/foodstuff}}</ul>{{/list}}';

// No precompilation step means Mustache.js is at a disadvantage
var mustache_func = Mustache.compile(mustache_template);


// -------------------------------------------------
// John Resig's Micro-Templating

function resig_micro(str) {
    var strFunc =
        "var p=[];p.push('" +
        str.replace(/[\r\t\n]/g, " ")
        .replace(/'(?=[^#]*#>)/g, "\t")
        .split("'").join("\\'")
        .split("\t").join("'")
        .replace(/<#=(.+?)#>/g, "',$1,'")
        .split("<#").join("');")
        .split("#>").join("p.push('")
        + "');return p.join('');";
    return new Function("data", strFunc);
}

// Precompile
var resig_micro_func = resig_micro(_template);



// -------------------------------------------------
// jquery.tmpl

// Template
var jquery_template = '<h2>{{html title}}</h2>{{each list}}<ul class="{{html liststyle}}">{{each foodstuff}}<li>{{html amount}} {{if unit}}{{html unit}} {{/if}}<strong>{{html ingredient}}</strong></li>{{/each}}</ul>{{/each}}';

// Precompile
var jquery_func = $.template(null, jquery_template);



// -------------------------------------------------
// Kendo UI Templates

// Precompile
var kendo_func = kendo.template(_template, {useWithBlock:false});

// -------------------------------------------------
// hogan.js 

var hogan_temp = '<h2>{{title}}</h2>{{#list}}<ul class="{{liststyle}}">{{#foodstuff}}<li>{{amount}} {{#unit}}{{unit}} {{/unit}}<strong>{{ingredient}}</strong></li>{{/foodstuff}}</ul>{{/list}}';

// Precompile
var hogan_template = Hogan.compile(hogan_temp);



// -------------------------------------------------
// T2 Templates

var T2={};
T2.Template = (function(){
		var interpolate = function(str,tmpls,parent) {
			return str
				.replace(/[\r\t\n]/g, " ")
				.replace(/'(?=[^#]*#\})/g, "\t")
				.split("'").join("\\'")
				.replace(/data\.(?=[^#]*#\})/g, parent+".")
				.split("\t").join("'")
				.replace(/\{=@(.+?)\}/g, function(a,b){ return sub(b,tmpls,parent); })
				.replace(/\{=(.+?)\}/g, "'+"+parent+".$1+'")
				.split("{#").join("';")
				.split("#}").join("p+='");
		};
		var sub = function(id,tmpls,parent) {
			var str = tmpls[id],
				indexVar = '_'+id,
				arrayVar = indexVar+'Arr',
				elemVar = indexVar+'Elem';
			return "';for(var "+indexVar+"=0,"+arrayVar+"="+parent+"."+id+"; "+indexVar+"<"+arrayVar+".length;"+indexVar+"++){var "+elemVar+"="+arrayVar+"["+indexVar+"];p+='"
				+ interpolate(str,tmpls,elemVar)
				+ "';}p+='";
		};
		return {
			precompile: function(tmpls) {
				var func = "var p='';function out(s){p+=s;}p+='"
					+ interpolate(tmpls['$'],tmpls,'data')
					+ "';return p;";
				return new Function("data", func);
			}
		};
})();

// Template
var t2_template = {
	$: '<h2>{=title}</h2>{=@list}',
	list: '<ul class="{=liststyle}">{=@foodstuff}</ul>',
	foodstuff: '<li>{=amount}{# if (data.unit) out(" "+data.unit); #} <strong>{=ingredient}</strong></li>'
};

// Precompile
var t2_func = T2.Template.precompile(t2_template);


</script>
    

Preparation code output

Desired output: <h2>Recipe for Brownies</h2> <ul class="glossy rounded"> <li>120 ml <strong>butter</strong></li> <li>120 ml <strong>unsweetened cocoa</strong></li> <li>240 ml <strong>sugar</strong></li> <li>2 <strong>eggs</strong></li> <li>10 ml <strong>vanilla</strong></li> <li>120 ml <strong>flour</strong></li> <li>1/4 tsp <strong>salt</strong></li> </ul>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Underscore.js Templates
underscore_func(the_data);
pending…
Mustache.js
mustache_func(the_data);
pending…
John Resig's Micro-Templating
resig_micro_func(the_data);
pending…
jquery.tmpl
jquery_func($,{data:the_data}).join("");
pending…
Kendo UI Templates
kendo_func(the_data);
pending…
T2 Templates
t2_func(the_data);
pending…
Lo-Dash Templates
lodash_func(the_data);
pending…
Hogan templates
hogan_template.render(the_data);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments

Desired output:

Recipe for Brownies