JS Template Engines Performance (v36)

Revision 36 of this benchmark created on


Description

Preparation HTML

<!-- Targeted DIV -->
<div id="target" style="display: none;"></div>

<!-- Libraries -->
<script src="https://code.jquery.com/jquery-git2.js"></script>
<script src="https://rawgithub.com/eugenioclrc/handlehash.js/master/HandleHash.js"></script>
<script src="https://documentcloud.github.io/underscore/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.2.1/handlebars.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hogan.js/2.0.0/hogan.min.js"></script>
<script src="https://rawgithub.com/olado/doT/master/doT.js"></script>
<script src="https://gsf.github.io/whiskers.js/whiskers.min.js"></script>
<script src="https://qatrix.com/js/qatrix-1.1.min.js"></script>
<script src="https://rawgithub.com/premasagar/875670/raw/d52752ead19a4eebc7237602438ae08a2541a5b5/tim-lite-cached-min.js"></script>
<script src="http://fb.me/react-0.8.0.min.js"></script>
<script src="http://dragon.ak.fbcdn.net/hphotos-ak-prn1/851547_221914424655081_22271_n.js"></script>

Setup

sharedVariables = {
            header: "Header",
            header2: "Header 2",
            header3: "Header 3",
            header4: "Header 4",
            header5: "Header 5",
            header6: "Header 6",
            list: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
        };
    
    var element = document.getElementById('target');
    
        hoganCompile = Hogan.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'>{{#list}}<li class='item'>{{{.}}}</li>{{/list}}</ul></div>");
    
    handleHashTemplate = HandleHash.build("<div><h1>{#=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(data.list,item) #}<li class='item'>{#= item #}</li>{#endfor #</ul></div>");
    
    
        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>";
    
    
        mustacheCompiledTemplate = Mustache.compile(mustacheTemplate);
    
    
        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>");
    
    
        underscoreTemplateEach = _.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'><% _.each(list, function(item){ %><li class='item'><%= item %></li><% }); %></ul></div>");
    
    
        underscoreTemplateEachCached = _.memoize(_.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'><% _.each(list, function(item){ %><li class='item'><%= item %></li><% }); %></ul></div>"), function (v) {
            return v
        });
    
        underscoreTemplateCached = _.memoize(_.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>"), function (v) {
            return v
        });
    
    
    
        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>");
    
    
        doTtemplate = doT.template("<div><h1 class='header'>{{=it.header}}</h1><h2 class='header2'>{{=it.header2}}</h2><h3 class='header3'>{{=it.header3}}</h3><h4 class='header4'>{{=it.header4}}</h4><h5 class='header5'>{{=it.header5}}</h5><h6 class='header6'>{{=it.header6}}</h6><ul class='list'>{{for(var i = 0,l=it.list.length;i<l;i++) { }}<li class='item'>{{=it.list[i]}}</li>{{ } }}</ul></div>");
    
        doTtemplate2 = doT.compile("<div><h1 class='header'>{{=it.header}}</h1><h2 class='header2'>{{=it.header2}}</h2><h3 class='header3'>{{=it.header3}}</h3><h4 class='header4'>{{=it.header4}}</h4><h5 class='header5'>{{=it.header5}}</h5><h6 class='header6'>{{=it.header6}}</h6><ul class='list'>{{~it.list :value:index}}<li class='item'>{{=value}}</li>{{~}}</ul></div>", {
            append: true
        });
    
        whiskersRender = whiskers.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'>{for item in list}<li class='item'>{item}</li>{/for}</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);
        }
    
        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 o='';" +
                "o='" +
                str.replace(/[\r\t\n]/g, " ")
                .replace(/'(?=[^#]*#>)/g, "\t")
                .split("'").join("\\'")
                .split("\t").join("'")
                .replace(/<#=(.+?)#>/g, "'+($1)+'")
                .split("<#").join("';")
                .split("#>").join("o+='") + "';return o;";
    
            return new Function("data", strFunc);
        }
    
        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>");
    
    var App = App || {};
        
        App.template = '<div>' +
                         '<h1 class="header"><%this.header%></h1>' +
                         '<h2 class="header2"><%this.header2%></h2>' +
                         '<h3 class="header3"><%this.header3%></h3>' +
                         '<h4 class="header4"><%this.header4%></h4>' +
                         '<h5 class="header5"><%this.header5%></h5>' +
                         '<h6 class="header6"><%this.header6%></h6>' +
                         '<ul class="list">' +
                           '<%for(var i in this.list) {%>' +
                             '<li class="item"><%this.list[i]%></li>' +
                           '<%}%>' +
                         '</ul>' +
                       +'</div>'
        App.render = function(html, options) {
            var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0;
            var add = function(line, js) {
                js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
                    (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
                return add;
            }
            while(match = re.exec(html)) {
                add(html.slice(cursor, match.index))(match[1], true);
                cursor = match.index + match[0].length;
            }
            add(html.substr(cursor, html.length - cursor));
            code += 'return r.join("");';
            return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
        }
    
    /** @jsx React.DOM */
    
    var List = React.createClass({
      render: function() {
        var createItem = function(itemText) {
          return <li>{itemText}</li>;
        };
        return <ul>{this.props.items.map(createItem)}</ul>;
      }
    });
    var Element = React.createClass({
      getInitialState: function() {
        return sharedVariables;
      },
      render: function() {
        return (
          <div>
            <h1 class='header'>{this.state.header}</h1>
            <h2 class='header'>{this.state.header2}</h2>
            <h3 class='header'>{this.state.header3}</h3>
            <h4 class='header'>{this.state.header4}</h4>
            <h5 class='header'>{this.state.header5}</h5>
            <h6 class='header'>{this.state.header6}</h6>
            <List items={this.state.list} />
          </div>
        );
      }
    });

Test runner

Ready to run.

Testing in
TestOps/sec
mustache.js
element.innerHTML = Mustache.render(mustacheTemplate, sharedVariables);
ready
underscore.js
element.innerHTML = underscoreTemplate(sharedVariables);
ready
handlebar.js
element.innerHTML = handlebarsTemplate(sharedVariables);
ready
Hogan.js
element.innerHTML = hoganCompile.render(sharedVariables);
ready
Resig Micro Templates (modified)
element.innerHTML = resig(sharedVariables);
ready
Resig Micro Templates (No "with" block)
element.innerHTML = resig2(sharedVariables);
ready
underscore.js (_.each)
element.innerHTML = underscoreTemplateEach(sharedVariables);
ready
Mustache (compiled)
element.innerHTML = mustacheCompiledTemplate(sharedVariables);
ready
HandleHash
element.innerHTML = handleHashTemplate(sharedVariables);
ready
underscore each cached
element.innerHTML = underscoreTemplateEachCached(sharedVariables);
ready
underscore for cached
element.innerHTML = underscoreTemplateCached(sharedVariables);
ready
doT.js
element.innerHTML = doTtemplate(sharedVariables);
ready
doT.js 2
element.innerHTML = doTtemplate2(sharedVariables);
ready
Whiskers.js
element.innerHTML = whiskersRender(sharedVariables);
ready
React
React.renderComponent(<Element />, target);
ready

Revisions

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