我正在使用Javascript(与Mootools)来使用HTML“模板”元素动态构建大页面,多次复制同一模板以填充页面。在每个模板中,我使用需要替换的字符串关键字来创建唯一的ID。但是,我遇到了严重的性能问题,因为执行所有这些替换都需要花费几秒钟的时间,尤其是在IE中。代码如下:

var fieldTemplate = $$('.fieldTemplate')[0];
var fieldTr = fieldTemplate.clone(true, true);
fieldTr.removeClass('fieldTemplate');
replaceIdsHelper(fieldTr, ':FIELD_NODE_ID:', fieldNodeId);
parentTable.grab(fieldTr);


根据IE9的分析器,replaceIdsHelper()是问题方法。我尝试了此方法的两种实现:

// Retrieve the entire HTML body of the element, replace the string and set the HTML back.
var html = rootElem.get('html').replace(new RegExp(replaceStr, 'g'), id);
rootElem.set('html', html);




// Load the child elements and replace just their IDs selectively
rootElem.getElements('*').each(function(elem) {
    var elemId = elem.get('id');
    if (elemId != null) elemId = elemId.replace(replaceStr, id);
    elem.set('id', elemId)
});


但是,考虑到调用此方法的次数(大约200 ...),这两种方法都非常慢。其他一切运行正常,只是替换了这些ID,这似乎是主要的性能瓶颈。有谁知道是否有一种方法可以有效地执行此操作,或者原因可能是运行如此缓慢?元素开始隐藏,直到创建后才被DOM抓取,因此不会发生重绘。

顺便说一下,我以这种方式构建页面的原因是保持代码干净,因为我们还需要能够在加载后动态创建新元素。从服务器端执行此操作会使事情复杂得多。

最佳答案

您可以做一些事情来优化它-@nizan tomer所说的非常好,伪模板是一个很好的模式。

首先。

var fieldTemplate = $$('.fieldTemplate')[0];
var fieldTr = fieldTemplate.clone(true, true);


您应该这样做:

var templateHTML = somenode.getElement(".fieldTemplate").get("html"); // no need to clone it.


模板本身应该/可以像建议的那样,例如:

<td id="{id}">{something}</td>


只读取一次,无需为每个项目都克隆它-而是使用新的Element构造函数并只设置innerHTML-注意它缺少<tr> </tr>

如果您有一个带有数据的对象,例如:

var rows = [{
    id: "row1",
    something: "hello"
}, {
    id: "row2",
    something: "there"
}];

Array.each(function(obj, index) {
    var newel = new Element("tr", {
        html: templateHTML.substitute(obj)
    });
    // defer the inject so it's non-blocking of the UI thread:
    newel.inject.delay(10, newel, parentTable);
    // if you need to know when done, use a counter + index
    // in a function and fire a ready.
});


或者,使用文档片段:

Element.implement({
    docFragment: function(){
        return document.createDocumentFragment();
    }
});

(function() {
    var fragment = Element.docFragment();

    Array.each(function(obj) {
        fragment.appendChild(new Element("tr", {
            html: templateHTML.substitute(obj)
        }));
    });

    // inject all in one go, single dom access
    parentTable.appendChild(fragment);
})();


我对这两种方法都进行了jsperf测试:
http://jsperf.com/inject-vs-fragment-in-mootools

chrome以惊人的优势与Firefox和ie9取得了令人惊讶的胜利。同样令人惊讶的是,在Firefox中,单个注入比片段更快。瓶颈可能是表格中的TR,这一直很不可靠。

对于模板:您还可以查看使用诸如mustache或underscore.js模板之类的东西。

10-05 21:05
查看更多