Backbone.js性能优化 #

一、性能问题 #

1.1 常见性能问题 #

text
性能问题
├── 频繁DOM操作
├── 过多事件监听
├── 内存泄漏
├── 大数据量渲染
└── 不必要的同步请求

1.2 性能检测 #

javascript
console.time('render');
view.render();
console.timeEnd('render');

二、渲染优化 #

2.1 使用文档片段 #

javascript
var UserListView = Backbone.View.extend({
    render: function() {
        var fragment = document.createDocumentFragment();
        
        this.collection.each(function(user) {
            var view = new UserView({ model: user });
            fragment.appendChild(view.render().el);
        });
        
        this.$el.empty().append(fragment);
        return this;
    }
});

2.2 减少DOM操作 #

javascript
var BadView = Backbone.View.extend({
    render: function() {
        this.$el.empty();
        
        this.collection.each(function(user) {
            this.$el.append('<li>' + user.get('name') + '</li>');
        }, this);
        
        return this;
    }
});

var GoodView = Backbone.View.extend({
    render: function() {
        var html = this.collection.map(function(user) {
            return '<li>' + user.get('name') + '</li>';
        }).join('');
        
        this.$el.html(html);
        return this;
    }
});

2.3 缓存DOM引用 #

javascript
var UserView = Backbone.View.extend({
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        
        this.$name = this.$('.name');
        this.$email = this.$('.email');
        this.$avatar = this.$('.avatar');
        
        return this;
    },
    
    updateName: function(name) {
        this.$name.text(name);
    }
});

2.4 批量更新 #

javascript
var UserView = Backbone.View.extend({
    initialize: function() {
        this.pendingUpdate = false;
        this.listenTo(this.model, 'change', this.scheduleUpdate);
    },
    
    scheduleUpdate: function() {
        if (!this.pendingUpdate) {
            this.pendingUpdate = true;
            requestAnimationFrame(this.render.bind(this));
        }
    },
    
    render: function() {
        this.pendingUpdate = false;
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

2.5 虚拟滚动 #

javascript
var VirtualListView = Backbone.View.extend({
    initialize: function(options) {
        this.itemHeight = options.itemHeight || 40;
        this.visibleCount = Math.ceil(this.$el.height() / this.itemHeight) + 2;
        this.startIndex = 0;
        
        this.$scrollContainer = options.scrollContainer || $(window);
        this.$scrollContainer.on('scroll', this.onScroll.bind(this));
    },
    
    onScroll: function() {
        var scrollTop = this.$scrollContainer.scrollTop();
        var newStartIndex = Math.floor(scrollTop / this.itemHeight);
        
        if (newStartIndex !== this.startIndex) {
            this.startIndex = newStartIndex;
            this.render();
        }
    },
    
    render: function() {
        var endIndex = Math.min(
            this.startIndex + this.visibleCount,
            this.collection.length
        );
        
        var fragment = document.createDocumentFragment();
        
        for (var i = this.startIndex; i < endIndex; i++) {
            var model = this.collection.at(i);
            var view = new ItemView({ model: model });
            fragment.appendChild(view.render().el);
        }
        
        this.$el.empty().append(fragment);
        this.$el.css('padding-top', this.startIndex * this.itemHeight);
        
        return this;
    }
});

三、事件优化 #

3.1 使用事件委托 #

javascript
var GoodView = Backbone.View.extend({
    events: {
        'click .item': 'onItemClick',
        'click .delete': 'onDeleteClick'
    },
    
    onItemClick: function(e) {
        var id = $(e.currentTarget).data('id');
    }
});

var BadView = Backbone.View.extend({
    render: function() {
        this.collection.each(function(item) {
            var $item = $('<div class="item">').text(item.get('name'));
            $item.on('click', function() {});
            this.$el.append($item);
        }, this);
    }
});

3.2 防抖和节流 #

javascript
var SearchView = Backbone.View.extend({
    events: {
        'input #search': 'onSearch'
    },
    
    initialize: function() {
        this.debouncedSearch = _.debounce(this.doSearch, 300);
    },
    
    onSearch: function(e) {
        this.debouncedSearch($(e.target).val());
    },
    
    doSearch: function(query) {
        this.collection.fetch({ data: { q: query } });
    }
});

3.3 及时解绑事件 #

javascript
var UserView = Backbone.View.extend({
    initialize: function() {
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.collection, 'add', this.addOne);
    },
    
    remove: function() {
        this.stopListening();
        Backbone.View.prototype.remove.call(this);
    }
});

3.4 避免内存泄漏 #

javascript
var LeakyView = Backbone.View.extend({
    initialize: function() {
        $(window).on('resize', this.onResize);
    },
    
    onResize: function() {
        this.render();
    }
});

var FixedView = Backbone.View.extend({
    initialize: function() {
        this.onResize = this.onResize.bind(this);
        $(window).on('resize', this.onResize);
    },
    
    onResize: function() {
        this.render();
    },
    
    remove: function() {
        $(window).off('resize', this.onResize);
        Backbone.View.prototype.remove.call(this);
    }
});

四、数据优化 #

4.1 分页加载 #

javascript
var PagedCollection = Backbone.Collection.extend({
    initialize: function() {
        this.page = 1;
        this.perPage = 20;
    },
    
    fetchNext: function() {
        return this.fetch({
            data: {
                page: this.page,
                per_page: this.perPage
            },
            remove: false
        });
    },
    
    parse: function(response) {
        this.total = response.total;
        this.hasMore = this.page * this.perPage < this.total;
        return response.data;
    }
});

4.2 懒加载 #

javascript
var LazyView = Backbone.View.extend({
    initialize: function() {
        this.loaded = false;
    },
    
    render: function() {
        if (!this.loaded) {
            this.loadContent();
        }
        return this;
    },
    
    loadContent: function() {
        var self = this;
        
        this.collection.fetch().done(function() {
            self.loaded = true;
            self.renderContent();
        });
    }
});

4.3 数据缓存 #

javascript
var CachedModel = Backbone.Model.extend({
    cache: {},
    
    fetch: function(options) {
        options = options || {};
        
        if (options.cache !== false) {
            var cached = this.getCache();
            if (cached) {
                this.set(cached);
                return $.Deferred().resolve().promise();
            }
        }
        
        var self = this;
        return Backbone.Model.prototype.fetch.call(this, options)
            .done(function() {
                self.setCache();
            });
    },
    
    getCache: function() {
        var cached = localStorage.getItem(this.url());
        if (cached) {
            var data = JSON.parse(cached);
            if (Date.now() - data.timestamp < 5 * 60 * 1000) {
                return data.value;
            }
        }
        return null;
    },
    
    setCache: function() {
        localStorage.setItem(this.url(), JSON.stringify({
            value: this.toJSON(),
            timestamp: Date.now()
        }));
    }
});

五、网络优化 #

5.1 批量请求 #

javascript
var BatchCollection = Backbone.Collection.extend({
    batchFetch: function(ids) {
        return $.ajax({
            url: this.url + '/batch',
            method: 'POST',
            data: JSON.stringify({ ids: ids }),
            contentType: 'application/json'
        });
    }
});

5.2 请求合并 #

javascript
var RequestBatcher = {
    pending: [],
    timer: null,
    delay: 50,
    
    add: function(request) {
        this.pending.push(request);
        
        if (!this.timer) {
            this.timer = setTimeout(this.flush.bind(this), this.delay);
        }
    },
    
    flush: function() {
        var batch = this.pending;
        this.pending = [];
        this.timer = null;
        
        $.ajax({
            url: '/api/batch',
            method: 'POST',
            data: JSON.stringify(batch),
            contentType: 'application/json'
        });
    }
};

六、总结 #

6.1 优化要点 #

优化项 方法
渲染 文档片段、缓存DOM
事件 委托、防抖、及时解绑
数据 分页、缓存、懒加载
网络 批量请求、合并请求

6.2 最佳实践 #

  1. 减少DOM操作次数
  2. 使用事件委托
  3. 及时清理事件监听
  4. 实现虚拟滚动处理大数据
  5. 合理使用缓存
最后更新:2026-03-28