JavaScript template language shootoff

JavaScript performance comparison

Revision 286 of this test case created by Todd Anglin

Info

A limited comparison of some popular JavaScript templating engines on a short template: 6 header tags, and 10 list items. Compared templating engines:

Understanding Kendo UI Template Performance

It's not magic. Through careful performance testing, we have found that string concatenation (+=) outside of a "with" block is significantly faster than JavaScript array push/join operations in a "with" block. This helps Kendo UI Templates run as fast as possible. Additional optimizations are coming soon to make performance in a "with" block better, too.

More on Kendo UI Templates

Note: When adding a new test, please ensure that your test returns the same HTML string (or equivalent DOM fragment) as the others.

Preparation code

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<script src="http://documentcloud.github.com/underscore/underscore.js"></script>

<!--NOTE: Need to load from CDNJS since GitHub uses text/plain MIME type, which is blocked in IE9-->
<!--<script src="http://github.com/janl/mustache.js/raw/master/mustache.js"></script>-->
<script src="http://ajax.cdnjs.com/ajax/libs/mustache.js/0.3.0/mustache.min.js"></script>

<script src="https://github.com/downloads/wycats/handlebars.js/handlebars.1.0.0.beta.3.js"></script>

<script src="http://cdn.kendostatic.com/2011.3.1129/js/kendo.all.min.js"></script>

<script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>

<!--NOTE: Need to load from MSFT CDN since GitHub uses text/plain MIME type, which is blocked in IE9-->
<!--<script src="http://github.com/jquery/jquery-tmpl/raw/master/jquery.tmpl.min.js"></script>-->
<script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>

<script src="https://raw.github.com/BorisMoore/jsrender/master/jsrender.js"></script>

<!--External Template Definitions-->
<script type="text/x-kendo-template" id="kendoUIextTemplate">
<div>
<h1 class='header'>#= data.header #</h1>
<h2 class='header2'>#= data.header2 #</h2>
<h3 class='header3'>#= data.header3 #</h3>
<h4 class='header4'>#= data.header4 #</h4>
<h5 class='header5'>#= data.header5 #</h5>
<h6 class='header6'>#= data.header6 #</h6>
<ul class='list'>
   # for (var i = 0, l = data.list.length; i < l; i++) { #
   <li class='item'>#= data.list[i] #</li>
   # } #
</ul>
</div>
</script>
<script>
  window.sharedVariables = {
   header: "Header",
   header2: "Header2",
   header3: "Header3",
   header4: "Header4",
   header5: "Header5",
   header6: "Header6",
   list: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
  };
 
//jQuery Templates superseded by JsRender
  //window.jQueryTemplate = $.template(null, "<div><h1 class='header'>{{html header}}</h1><h2 class='header2'>{{html header2}}</h2><h3 class='header3'>{{html header3}}</h3><h4 class='header4'>{{html header4}}</h4><h5 class='header5'>{{html header5}}</h5><h6 class='header6'>{{html header6}}</h6><ul class='list'>{{each list}}<li class='item'>{{html $value}}</li>{{/each}}</ul></div>");

//JsRender compiled template (no encoding)
$.template("jsperf","<div><h1 class='header'>{{= header !}}</h1><h2 class='header2'>{{= header2 !}}</h2><h3 class='header3'>{{= header3 !}}</h3><h4 class='header4'>{{= header4 !}}</h4><h5 class='header5'>{{= header5 !}}</h5><h6 class='header6'>{{= header6 !}}</h6><ul class='list'>{{#each list}}<li class='item'>{{= $value !}}</li>{{/each}}</ul></div>");
 
  window.mustacheTemplate = "<div><h1 class='header'>{{{header}}}</h1><h2 class='header2'>{{{header2}}}</h2><h3 class='header3'>{{{header3}}}</h3><h4 class='header4'>{{{header4}}}</h4><h5 class='header5'>{{{header5}}}</h5><h6 class='header6'>{{{header6}}}</h6><ul class='list'>{{#list}}<li class='item'>{{{.}}}</li>{{/list}}</ul></div>";
 
  window.handlebarsTemplate = Handlebars.compile("<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#each list}}<li class='item'>{{this}}</li>{{/each}}</ul></div>");
 
  window.kendouiTemplate = kendo.template("<div><h1 class='header'>#= data.header #</h1><h2 class='header2'>#= data.header2 #</h2><h3 class='header3'>#= data.header3 #</h3><h4 class='header4'>#= data.header4 #</h4><h5 class='header5'>#= data.header5 #</h5><h6 class='header6'>#= data.header6 #</h6><ul class='list'># for (var i = 0, l = data.list.length; i < l; i++) { #<li class='item'>#= data.list[i] #</li># } #</ul></div>", {useWithBlock:true});
 
  window.kendouiTemplate2 = kendo.template("<div><h1 class='header'>#= data.header #</h1><h2 class='header2'>#= data.header2 #</h2><h3 class='header3'>#= data.header3 #</h3><h4 class='header4'>#= data.header4 #</h4><h5 class='header5'>#= data.header5 #</h5><h6 class='header6'>#= data.header6 #</h6><ul class='list'># for (var i = 0, l = data.list.length; i < l; i++) { #<li class='item'>#= data.list[i] #</li># } #</ul></div>", {useWithBlock:false});
 
  //Use external template definition
  window.kendoUIAlt = kendo.template($("#kendoUIextTemplate").html());
  window.kendoUIAlt2 = kendo.template($("#kendoUIextTemplate").html(), {useWithBlock:false});
 
  window.underscoreTemplate = _.template("<div><h1 class='header'><%= header %></h1><h2 class='header2'><%= header2 %></h2><h3 class='header3'><%= header3 %></h3><h4 class='header4'><%= header4 %></h4><h5 class='header5'><%= header5 %></h5><h6 class='header6'><%= header6 %></h6><ul class='list'><% for (var i = 0, l = list.length; i < l; i++) { %><li class='item'><%= list[i] %></li><% } %></ul></div>");
 
  window.baseHtml = "<div><h1 class='header'></h1><h2 class='header2'></h2><h3 class='header3'></h3><h4 class='header4'></h4><h5 class='header5'></h5><h6 class='header6'></h6><ul class='list'><li class='item'></li></ul></div>";
 
  //Resig Template Function (modified to support ')
  function tmpl(str) {
              var strFunc =
              "var p=[];" +
                          "with(obj){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("obj", strFunc);
          }
 
  window.resig = tmpl("<div><h1 class='header'><#= header #></h1><h2 class='header2'><#= header2 #></h2><h3 class='header3'><#= header3 #></h3><h4 class='header4'><#= header4 #></h4><h5 class='header5'><#= header5 #></h5><h6 class='header6'><#= header6 #></h6><ul class='list'><# for (var i = 0, l = list.length; i < l; i++) { #><li class='item'><#= list[i] #></li><# } #></ul></div>");
 
  //Resig modified template function (no "with" block)
  function tmpl2(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);
          }
 
  window.resig2 = tmpl2("<div><h1 class='header'><#= data.header #></h1><h2 class='header2'><#= data.header2 #></h2><h3 class='header3'><#= data.header3 #></h3><h4 class='header4'><#= data.header4 #></h4><h5 class='header5'><#= data.header5 #></h5><h6 class='header6'><#= data.header6 #></h6><ul class='list'><# for (var i = 0, l = data.list.length; i < l; i++) { #><li class='item'><#= data.list[i] #></li><# } #></ul></div>");
</script>

Preparation code output

Test runner

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

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Mustache.js Template
Mustache.to_html(mustacheTemplate, sharedVariables);
pending…
JsRender
//Replaces jQuery Templates
$.render(sharedVariables, "jsperf");

//jQueryTemplate($, {
// data: sharedVariables
//}).join("");
pending…
Kendo UI Templates (Default)
kendouiTemplate(sharedVariables);
pending…
Kendo UI Templates (No "with" block)
kendouiTemplate2(sharedVariables)
pending…
Kendo UI Templates (External Temp Def)
kendoUIAlt(sharedVariables);
pending…
Kendo UI Templates (External Temp Def - No "with" block))
kendoUIAlt2(sharedVariables);
pending…
Handlebars.js
handlebarsTemplate(sharedVariables);
pending…
Underscore.js Template
underscoreTemplate(sharedVariables);
pending…
Resig Micro Templates (modified)
resig(sharedVariables)
pending…
Resig Micro Templates (No "with" block)
resig2(sharedVariables)
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. Here’s a list of current revisions for this page:

2 comments

Danny commented :

Why was jqote2 and doT (the reigning champs) removed from these comparisons? Naturally as a rep for Kendo UI you have an agenda, but your results would be more impressive if you compared all best options out there, without cherry picking.

dsad commented :

dsfdsfdsfsf

Add a comment