template perf compare

JavaScript performance comparison

Test case created

Preparation code

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="https://rawgithub.com/janl/mustache.js/master/mustache.js"></script>
<script src="https://rawgithub.com/linkedin/dustjs/master/dist/dust-full-2.0.2.js"></script>
<script src="https://rawgithub.com/linkedin/dustjs-helpers/master/dist/dust-helpers-1.1.1.js"></script>
<script src="http://rawgithub.com/olado/doT/master/doT.js"></script>
<script src="https://rawgithub.com/twitter/hogan.js/master/web/builds/2.0.0/hogan-2.0.0.min.js"></script>

<script>
!function() {

var applyFns = {
        extend: function(curVal, newVal) {
                return $.extend(curVal, newVal)
        },
        parseAndExtend: function(curVal, newVal) {
                var newObj = {}
                        , pairs = newVal.split(',')
                for (var i = 0; i < pairs.length; i++) {
                        var pair = pairs[i].split(':')
                                , key = $.trim(pair[0])
                                , val = $.trim(pair[1])
                        newObj[key] = val
                }

                return $.extend(curVal, newObj)
        }
}

var applyOrder = {
        'class': function(curVal, newVal) {
                return curVal ? curVal += ' ' + newVal : newVal
        },
        mix: function(curVal, newVal) {
                curVal.push(newVal)
                return curVal
        },
        attrs: applyFns.parseAndExtend,
        mods: applyFns.parseAndExtend,
        params: applyFns.extend
}

function apply(field, currVal, newVal) {
        if (applyOrder[field]) {
                return applyOrder[field](currVal, newVal)
        } else {
                return newVal
        }
}

function readParams(node, params, chunk, context) {
        for (var key in node) {
                var read = params[key]
                read = dust.helpers.tap(read, chunk, context)

                if (read) node[key] = apply(key, node[key], read)
        }
        return node
}

function readContext(node, context) {
        for (var key in node) {
                var read = context.get(key)
                if (read) node[key] = apply(key, node[key], read)
        }
        return node
}

function makeClass(node) {
        var classList = []
        if (node['class']) classList.push(node['class'])
        classList.push(makeBemClass(node))
        return classList.join(' ')
}

function makeBemClass(node) {
        var bem = node.block + (node.elem ? '__' + node.elem : '')
                , classList = [ bem ]

        if (node.mods) {
                for (var mod in node.mods) {
                        var modCls = bem + '_' + mod + '_' + node.mods[mod]
                        classList.push(modCls)
                }
        }

        for (var i = 0; i < node.mix.length; i++)
                classList.push(makeClass(node.mix[i]))
       
        if (node.js) classList.push('i-bem')

        return classList.join(' ')
}

function renderAttrs(attrs) {
        var res = []
        for (var a in attrs)
                res.push(a + '="' + attrs[a] + '"')
        //TODO onclickattr
        return res.join(' ')
}

function render(node, chunk, context, body) {
        var cls = makeClass(node)
        node.attrs['class'] = cls
        var attrs = renderAttrs(node.attrs)

        return chunk.write(
                '<' + node.tag + ' ' + attrs + '>' +
                node.content +
                '</' + node.tag + '>'
        )
}

dust.helpers.node = function(chunk, context, body, params) {
        var node = {
                block: '',
                elem: false,
                tag: 'div',
                attrs: {},
                mods: {},
                'class': '',
                mix: [],
                js: false,
                params: {}
        }

        node = readParams(node, params, chunk, context)
        if (params.main) node = readContext(node, context) //нужно делать tap в хелпере
       
        context = dust.makeBase($.extend({}, context.global, node))

        node.content = ''
        chunk
                .tap(function(data) { node.content += data; return '' })
                .render(body.block, context)
                .untap()

        var global = context.global
        if (params.main) {
                for (var key in global) {
                        if (key == 'content') continue;
                        var oldVal = node[key]
                                , newVal = global[key]
                        if ((oldVal !== undefined) && (newVal != oldVal))
                                node[key] = newVal
                }
        }

        return render(node, chunk, context, body)
}

dust.helpers.bem = function(chunk, context, bodies, params) {
  var partial = {
                isPartial: true,
                content: dust.helpers.tap(bodies.block, chunk, context) || ''
        }

        $.extend(partial, params)
 
        var template = params.block
        if (params.elem) template += '__' + params.elem

        if (dust.cache[template]) {
                return chunk.partial(template, dust.makeBase(partial))
        } else {
                var body = { block: function(chk, ctx) { return chk.write(partial.content) } }
                return chunk.helper('node', context, body, params)
        }
}

dust.helpers.param = function(chunk, context, bodies, params) {
        if (context.global.isPartial) {
                var key = params.key
                        , oldVal = context.global[key]
                        , newVal = params.val
                context.global[key] = apply(key, oldVal, newVal)
        }

        return chunk
}

dust.helpers.hasMod = function(chunk, context, bodies, params) {
        var mod = params.mod
                , val = context.global.mods[mod]
                , expectedVal = params.val
                , result = (expectedVal !== undefined) ? (val == expectedVal) : true
       
        if (result)
                return chunk.render(bodies.block, context)
        else
                return chunk
}

}();
</script>


<script type="text/html" class="mustache__template">
        {{#button}}
                {{>button}}
        {{/button}}
</script>

<script type="text/html" class="mustache__partial">
        <div class="button {{#size}}button_size_{{size}}{{/size}}">
                {{#icon}}
                        <div class="button__icon icon icon__{{name}}"
                {{/icon}}
                {{text}}
        </div>
</script>

<script type="text/html" class="dust__template">
        {#button}
                {>button1/}
        {/button}
</script>

<script type="text/html" class="dust__partial">
        <div class="button {#size}button_size_{size}{/size}">
                {#icon}
                        <div class="button__icon icon icon__{name}"
                {/icon}
                {text}
        </div>
</script>

<script type="text/html" class="dust__bemtemplate">
        {@bem block="button" mods="size: big"}
                {@bem block="button" elem="icon" class="icon icon__{icon}"}{/bem}
                {text}
        {/bem}
</script>

<script type="text/html" class="dust__bempartial">
        {@node block="button" main="yes"}
                {content|s}
        {/node}
</script>

<script>
        var mustachePrepare, dustPrepare, dustBemPrepare, hoganPrepare
        $(function() {
        mustachePrepare = {
                template: Mustache.compile($('.mustache__template').html()),
                partials: {
                        button: $('.mustache__partial').html()
                },
                data: {
                        button: {
                                text: 'hello',
                                size: 'big',
                                icon: {
                                        name: 'arrow'
                                }
                        }
                }
        }

        hoganPrepare = {
                template: Hogan.compile($('.mustache__template').html()),
                partials: {
                        button: Hogan.compile($('.mustache__partial').html())
                },
                data: {
                        button: {
                                text: 'hello',
                                size: 'big',
                                icon: {
                                        name: 'arrow'
                                }
                        }
                }
        }

        dust.loadSource(dust.compile($('.dust__template').html(), 'template'))
        dust.loadSource(dust.compile($('.dust__partial').html(), 'button1'))
        dustPrepare = {
                data: {
                        button: {
                                text: 'hello',
                                size: 'big',
                                icon: {
                                        name: 'arrow'
                                }
                        }
                }
        }

        dust.loadSource(dust.compile($('.dust__bemtemplate').html(), 'bemtemplate'))
        dust.loadSource(dust.compile($('.dust__bempartial').html(), 'button'))
        dustBemPrepare = {
                data: {
                        icon: 'arrow',
                        text: 'hello'
                }
        }

        })
</script>

<script>
var res = ''

function mustacheRender() {
                mustachePrepare.template(mustachePrepare.data, mustachePrepare.partials)
}

function hoganRender() {
                hoganPrepare.template.render(hoganPrepare.data, hoganPrepare.partials)
}

function dustRender() {
        dust.render('template', dustPrepare.data, function(err, out) {
/*              console.log(out)*/
        })
}

function dustBemRender() {
        dust.render('bemtemplate', dustBemPrepare.data, function(err, out) {
/*              console.log(out)*/
        })
}

function profileDust() {
        console.profile('dust');
        for (var i=0; i<5000; i++)
                dustRender();
        console.profileEnd('dust')
}
function profileDustBem() {
        console.profile('dustbem');
        for (var i=0; i<5000; i++)
                dustBemRender();
        console.profileEnd('dustbem')
}
</script>
 
<script>
Benchmark.prototype.setup = function() {
    var res = ''
   
};
</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

mustachePrepare.template(mustachePrepare.data, mustachePrepare.partials)
 
pending…
dust

        dust.render('template', dustPrepare.data, function(err, out) {
        })
 
pending…
dust-bem

        dust.render('bemtemplate', dustBemPrepare.data, function(err, out) {
        })
pending…
hogan

                hoganPrepare.template.render(hoganPrepare.data, hoganPrepare.partials)
 
pending…

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

Compare results of other browsers

0 comments

Add a comment