Backbone.js模板引擎 #
一、模板概述 #
1.1 什么是模板 #
模板是将数据与HTML结构分离的技术,通过占位符动态生成HTML。
text
模板 = HTML结构 + 数据占位符
优势:
├── 分离数据和视图
├── 提高代码可维护性
├── 便于复用
└── 支持预编译
1.2 Backbone与模板 #
Backbone.js 不强制使用特定模板引擎,默认使用 Underscore.js 的模板功能。
javascript
var UserView = Backbone.View.extend({
template: _.template('<h1><%= name %></h1>'),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
二、Underscore模板 #
2.1 基本语法 #
javascript
var template = _.template('<h1><%= name %></h1>');
var html = template({ name: '张三' });
console.log(html);
2.2 三种语法 #
| 语法 | 说明 | 示例 |
|---|---|---|
<%= %> |
输出值(不转义) | <%= name %> |
<%- %> |
输出值(HTML转义) | <%- content %> |
<% %> |
执行JavaScript | <% if (show) { %> |
2.3 输出值 #
javascript
var template = _.template(
'<div>' +
' <p>不转义: <%= html %></p>' +
' <p>转义: <%- html %></p>' +
'</div>'
);
var html = template({ html: '<strong>粗体</strong>' });
console.log(html);
2.4 条件判断 #
javascript
var template = _.template(
'<div>' +
'<% if (user) { %>' +
' <p>欢迎, <%= user.name %>!</p>' +
'<% } else { %>' +
' <p>请登录</p>' +
'<% } %>' +
'</div>'
);
console.log(template({ user: { name: '张三' } }));
console.log(template({ user: null }));
2.5 循环 #
javascript
var template = _.template(
'<ul>' +
'<% items.forEach(function(item) { %>' +
' <li><%= item.name %></li>' +
'<% }); %>' +
'</ul>'
);
var html = template({
items: [
{ name: '项目1' },
{ name: '项目2' },
{ name: '项目3' }
]
});
console.log(html);
2.6 模板变量 #
javascript
var template = _.template(
'<% _.each(items, function(item) { %>' +
' <li><%= item %></li>' +
'<% }); %>'
);
var html = template({
items: ['A', 'B', 'C']
});
2.7 自定界符 #
javascript
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g
};
var template = _.template('<h1>{{ name }}</h1>');
console.log(template({ name: '张三' }));
2.8 变量作用域 #
javascript
var template = _.template(
'<p><%= name %></p>',
{ variable: 'data' }
);
var html = template({ name: '张三' });
三、外部模板 #
3.1 Script标签模板 #
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;
}
});
3.2 模板缓存 #
javascript
var TemplateCache = {
templates: {},
get: function(id) {
if (!this.templates[id]) {
var $template = $('#' + id);
if ($template.length) {
this.templates[id] = _.template($template.html());
}
}
return this.templates[id];
}
};
var UserView = Backbone.View.extend({
render: function() {
var template = TemplateCache.get('user-template');
this.$el.html(template(this.model.toJSON()));
return this;
}
});
3.3 异步加载模板 #
javascript
var TemplateLoader = {
cache: {},
load: function(url) {
var self = this;
if (this.cache[url]) {
return $.Deferred().resolve(this.cache[url]);
}
return $.get(url).then(function(html) {
var template = _.template(html);
self.cache[url] = template;
return template;
});
}
};
var UserView = Backbone.View.extend({
initialize: function() {
var self = this;
TemplateLoader.load('/templates/user.html').done(function(template) {
self.template = template;
self.render();
});
},
render: function() {
if (this.template) {
this.$el.html(this.template(this.model.toJSON()));
}
return this;
}
});
四、Handlebars模板 #
4.1 引入Handlebars #
html
<script src="handlebars.js"></script>
4.2 基本使用 #
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;
}
});
4.3 Handlebars语法 #
javascript
var template = Handlebars.compile(
'<div class="user">' +
' <h2>{{name}}</h2>' +
' <p>{{email}}</p>' +
' {{#if admin}}' +
' <span class="badge">管理员</span>' +
' {{/if}}' +
' {{#each tags}}' +
' <span class="tag">{{this}}</span>'' +
' {{/each}}' +
'</div>'
);
4.4 Handlebars助手 #
javascript
Handlebars.registerHelper('uppercase', function(str) {
return str.toUpperCase();
});
Handlebars.registerHelper('formatDate', function(date) {
return new Date(date).toLocaleDateString();
});
var template = Handlebars.compile(
'<p>{{uppercase name}}</p>' +
'<p>{{formatDate createdAt}}</p>'
);
五、Mustache模板 #
5.1 引入Mustache #
html
<script src="mustache.js"></script>
5.2 基本使用 #
javascript
var UserView = Backbone.View.extend({
template:
'<div class="user">' +
' <h2>{{name}}</h2>' +
' <p>{{email}}</p>' +
'</div>',
render: function() {
var html = Mustache.render(this.template, this.model.toJSON());
this.$el.html(html);
return this;
}
});
5.3 Mustache语法 #
javascript
var template =
'<div>' +
' <h2>{{name}}</h2>' +
' {{#admin}}<span>管理员</span>{{/admin}}' +
' {{^admin}}<span>普通用户</span>{{/admin}}' +
' <ul>' +
' {{#tags}}<li>{{.}}</li>{{/tags}}' +
' </ul>' +
'</div>';
var html = Mustache.render(template, {
name: '张三',
admin: true,
tags: ['JavaScript', 'Backbone']
});
六、模板组织 #
6.1 模板文件结构 #
text
/templates/
├── users/
│ ├── user-item.html
│ ├── user-list.html
│ └── user-form.html
├── todos/
│ ├── todo-item.html
│ └── todo-list.html
└── common/
├── header.html
└── footer.html
6.2 模板管理器 #
javascript
var TemplateManager = {
templates: {},
load: function(templates, callback) {
var self = this;
var deferreds = [];
templates.forEach(function(name) {
if (!self.templates[name]) {
deferreds.push(
$.get('/templates/' + name + '.html')
.done(function(html) {
self.templates[name] = _.template(html);
})
);
}
});
$.when.apply($, deferreds).done(callback);
},
get: function(name) {
return this.templates[name];
}
};
6.3 预编译模板 #
javascript
var JST = {
'users/item': _.template('<div class="user"><%= name %></div>'),
'users/list': _.template('<ul><% users.forEach(function(u) { %><li><%= u.name %></li><% }); %></ul>')
};
var UserView = Backbone.View.extend({
template: JST['users/item'],
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
七、实用示例 #
7.1 模板视图基类 #
javascript
var TemplateView = Backbone.View.extend({
templateName: null,
getTemplate: function() {
if (!this.template && this.templateName) {
this.template = TemplateManager.get(this.templateName);
}
return this.template;
},
getTemplateData: function() {
if (this.model) {
return this.model.toJSON();
}
if (this.collection) {
return { items: this.collection.toJSON() };
}
return {};
},
render: function() {
var template = this.getTemplate();
if (template) {
this.$el.html(template(this.getTemplateData()));
}
return this;
}
});
var UserView = TemplateView.extend({
templateName: 'users/item'
});
7.2 集合模板视图 #
javascript
var CollectionTemplateView = Backbone.View.extend({
itemView: null,
itemTemplateName: null,
render: function() {
this.$el.empty();
var template = TemplateManager.get(this.itemTemplateName);
this.collection.each(function(model) {
var html = template(model.toJSON());
this.$el.append(html);
}, this);
return this;
}
});
7.3 带助手的模板视图 #
javascript
var HelperTemplateView = Backbone.View.extend({
templateHelpers: {},
getTemplateData: function() {
var data = this.model ? this.model.toJSON() : {};
_.extend(data, this.templateHelpers, {
formatDate: function(date) {
return new Date(date).toLocaleDateString();
},
formatCurrency: function(amount) {
return '¥' + amount.toFixed(2);
}
});
return data;
},
render: function() {
this.$el.html(this.template(this.getTemplateData()));
return this;
}
});
八、总结 #
8.1 模板引擎对比 #
| 引擎 | 特点 | 适用场景 |
|---|---|---|
| Underscore | 内置,简单 | 简单项目 |
| Handlebars | 功能强大,预编译 | 大型项目 |
| Mustache | 轻量,多语言 | 跨语言项目 |
| EJS | 类似JSP | 熟悉JSP的团队 |
8.2 最佳实践 #
- 使用外部模板文件分离HTML
- 使用模板缓存提高性能
- 预编译模板减少运行时开销
- 统一模板管理方式
- 合理使用模板助手
最后更新:2026-03-28