使用 $.html() 时如何提高渲染性能

How to improve rendering performance when using $.html()

我正在开发一个 Backbone demo app 来显示推文列表。当我用不同的数据替换所有 "tweets" 时,我使用 $.html()

清除列表
render: function() {
    $("#item-table").html('');
    this.collection.each(this.addItem);
}

我想知道是否有人可以给我一个提示,我可以用什么来替换这个 $.html() 以获得更好的性能,因为使用 $.html() 我会导致回流,这会导致布局处理时间不佳。

我在代码中还有另外两个地方使用了 $.html(),如果其他地方可能的话,如果有人能给我建议如何更改这些地方,那就太好了。

Create a new DocumentFragment 预渲染所有项目,然后更新 DOM 一次。

此外,this.$(...) 优于全局 jQuery select 或 $(...)

this.$this.$el.find(...) 的代理,效率更高,并且不易出现 select 视图之外的东西。

如果尚未呈现视图,则在视图中使用 jQuery 的核心函数 ($()) 可能会失败。因此,最好始终通过 this.$el 进行操作,这样您甚至可以在视图实际放入 DOM.

之前进行更改

将创建的所有子视图保存在一个数组中,以便稍后彻底删除它们。

initialize: function() {
    this.childViews = [];
},
render: function() {
    // cache the list jQuery object
    this.$list = this.$("#item-table");

    // Make sure to destroy every child view explicitely 
    // to avoid memory leaks
    this.cleanup();

    this.renderCollection();
    return this;
},

真正的优化从这里开始,使用临时容器。

renderCollection: function() {
    var container = document.createDocumentFragment();

    this.collection.each(function(model) {
        // this appends to a in memory document
        container.appendChild(this.renderItem(model, false).el);
    }, this);

    // Update the DOM only once every child view was rendered.
    this.$list.html(container);
    return this;
},

我们的 renderItem 函数仍然可以用来渲染单个项目视图并立即将其放入 DOM。但它也提供了推迟 DOM 操作的选项,它只是 returns 视图。

renderItem: function(model, render) {
    var view = new Item({ model: model });

    this.childViews.push(view);
    view.render();
    if (render !== false) this.$list.append(view.el);
    return view;
},

为了避免悬垂侦听器的内存泄漏,重要的是在每个视图上调用 remove,然后再忘记它。

我通过推迟对 remove 的实际调用来使用额外的优化,这样我们现在就不会在用户等待时浪费时间。

cleanup: function() {
    var _childViewsDump = [].concat(this.childViews);
    this.childViews = [];

    while (_childViewsDump.length > 0) {
        var currentView = _childViewsDump.shift();
        // defer the removal as it's less important for now than rendering.
        _.defer(currentView.remove.bind(currentView), options);
    }
}