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 最佳实践 #
- 减少DOM操作次数
- 使用事件委托
- 及时清理事件监听
- 实现虚拟滚动处理大数据
- 合理使用缓存
最后更新:2026-03-28