Backbone.js视图渲染 #
一、渲染基础 #
1.1 render方法 #
render 是视图的核心方法,负责将模型数据渲染为HTML:
javascript
var UserView = Backbone.View.extend({
render: function() {
this.$el.html('<h1>' + this.model.get('name') + '</h1>');
return this;
}
});
1.2 返回this #
render 方法应该返回 this 以支持链式调用:
javascript
var view = new UserView({ model: user });
$('#app').html(view.render().el);
1.3 渲染时机 #
javascript
var UserView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, 'change', this.render);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
二、模板引擎 #
2.1 Underscore模板 #
Backbone.js 默认使用 Underscore.js 的模板引擎:
javascript
var UserView = Backbone.View.extend({
template: _.template(
'<div class="user">' +
' <h2><%= name %></h2>' +
' <p><%= email %></p>' +
'</div>'
),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
2.2 模板语法 #
| 语法 | 说明 |
|---|---|
<%= %> |
输出转义后的值 |
<%- %> |
输出HTML转义后的值 |
<% %> |
执行JavaScript代码 |
2.3 模板变量 #
javascript
var template = _.template(
'<% if (name) { %>' +
' <h2><%= name %></h2>' +
'<% } else { %>' +
' <h2>匿名用户</h2>' +
'<% } %>'
);
var html = template({ name: '张三' });
2.4 循环渲染 #
javascript
var template = _.template(
'<ul>' +
'<% items.forEach(function(item) { %>' +
' <li><%= item.name %></li>' +
'<% }); %>' +
'</ul>'
);
var html = template({
items: [
{ name: '项目1' },
{ name: '项目2' }
]
});
2.5 外部模板 #
html
<script type="text/template" id="user-template">
<div class="user">
<h2><%= name %></h2>
<p><%= email %></p>
</div>
</script>
javascript
var UserView = Backbone.View.extend({
template: _.template($('#user-template').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
2.6 Handlebars模板 #
javascript
var UserView = Backbone.View.extend({
template: Handlebars.compile(
'<div class="user">' +
' <h2>{{name}}</h2>' +
' <p>{{email}}</p>' +
'</div>'
),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
三、渲染策略 #
3.1 完整渲染 #
每次重新渲染整个视图:
javascript
var UserView = Backbone.View.extend({
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
3.2 局部渲染 #
只更新变化的部分:
javascript
var UserView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, 'change:name', this.updateName);
this.listenTo(this.model, 'change:email', this.updateEmail);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
updateName: function(model, value) {
this.$('.name').text(value);
},
updateEmail: function(model, value) {
this.$('.email').text(value);
}
});
3.3 条件渲染 #
javascript
var UserView = Backbone.View.extend({
render: function() {
var data = this.model.toJSON();
this.$el.html(this.template({
name: data.name,
email: data.email,
isAdmin: data.role === 'admin',
lastLogin: this.formatDate(data.lastLogin)
}));
return this;
},
formatDate: function(date) {
return new Date(date).toLocaleDateString();
}
});
3.4 延迟渲染 #
javascript
var UserView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, 'change', this.debouncedRender);
},
debouncedRender: _.debounce(function() {
this.render();
}, 100),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
四、集合渲染 #
4.1 渲染列表 #
javascript
var UserListView = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.$el.empty();
this.collection.each(function(user) {
var userView = new UserView({ model: user });
this.$el.append(userView.render().el);
}, this);
return this;
}
});
4.2 子视图管理 #
javascript
var UserListView = Backbone.View.extend({
initialize: function() {
this.childViews = [];
this.listenTo(this.collection, 'add', this.addOne);
this.listenTo(this.collection, 'remove', this.removeOne);
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.clearChildViews();
this.collection.each(this.addOne, this);
return this;
},
addOne: function(user) {
var view = new UserView({ model: user });
this.childViews.push(view);
this.$el.append(view.render().el);
},
removeOne: function(user) {
var view = _.find(this.childViews, function(v) {
return v.model === user;
});
if (view) {
view.remove();
this.childViews = _.without(this.childViews, view);
}
},
clearChildViews: function() {
this.childViews.forEach(function(view) {
view.remove();
});
this.childViews = [];
},
remove: function() {
this.clearChildViews();
Backbone.View.prototype.remove.call(this);
}
});
4.3 增量渲染 #
javascript
var UserListView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.collection, 'add', this.addOne);
this.listenTo(this.collection, 'remove', this.removeOne);
},
addOne: function(user) {
var view = new UserView({ model: user });
this.$el.append(view.render().el);
},
removeOne: function(user) {
this.$('[data-id="' + user.id + '"]').remove();
}
});
五、性能优化 #
5.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;
}
});
5.2 缓存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');
return this;
},
updateName: function(name) {
this.$name.text(name);
},
updateEmail: function(email) {
this.$email.text(email);
}
});
5.3 批量更新 #
javascript
var UserListView = Backbone.View.extend({
initialize: function() {
this.pendingUpdate = false;
this.listenTo(this.collection, '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({
users: this.collection.toJSON()
}));
return this;
}
});
5.4 虚拟滚动 #
javascript
var VirtualListView = Backbone.View.extend({
initialize: function(options) {
this.itemHeight = options.itemHeight || 50;
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;
}
});
六、实用示例 #
6.1 可复用渲染器 #
javascript
var Renderer = {
render: function(template, data) {
return template(data);
},
renderCollection: function(collection, itemView, container) {
var fragment = document.createDocumentFragment();
collection.each(function(model) {
var view = new itemView({ model: model });
fragment.appendChild(view.render().el);
});
container.empty().append(fragment);
}
};
var UserListView = Backbone.View.extend({
render: function() {
Renderer.renderCollection(this.collection, UserView, this.$el);
return this;
}
});
6.2 条件渲染 #
javascript
var ConditionalView = Backbone.View.extend({
render: function() {
if (this.model.isNew()) {
this.renderNew();
} else if (this.model.hasChanged()) {
this.renderChanged();
} else {
this.renderNormal();
}
return this;
},
renderNew: function() {
this.$el.html(this.newTemplate(this.model.toJSON()));
},
renderChanged: function() {
this.$el.html(this.changedTemplate(this.model.toJSON()));
},
renderNormal: function() {
this.$el.html(this.template(this.model.toJSON()));
}
});
6.3 异步模板加载 #
javascript
var AsyncTemplateView = Backbone.View.extend({
initialize: function() {
this.templateLoaded = false;
this.loadTemplate();
},
loadTemplate: function() {
var self = this;
$.get('/templates/user.html').done(function(html) {
self.template = _.template(html);
self.templateLoaded = true;
self.render();
});
},
render: function() {
if (this.templateLoaded) {
this.$el.html(this.template(this.model.toJSON()));
} else {
this.$el.html('<div class="loading">加载中...</div>');
}
return this;
}
});
七、总结 #
7.1 渲染方法 #
| 方法 | 说明 |
|---|---|
| html() | 替换内容 |
| append() | 追加内容 |
| prepend() | 前置内容 |
| empty() | 清空内容 |
7.2 模板引擎 #
| 引擎 | 特点 |
|---|---|
| Underscore | 内置,简单 |
| Handlebars | 功能强大,预编译 |
| Mustache | 轻量,多语言支持 |
| EJS | 类似JSP语法 |
7.3 最佳实践 #
- 使用模板引擎分离HTML
- 返回
this支持链式调用 - 使用文档片段优化性能
- 缓存DOM引用
- 合理使用增量渲染
最后更新:2026-03-28