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 最佳实践 #

  1. 使用外部模板文件分离HTML
  2. 使用模板缓存提高性能
  3. 预编译模板减少运行时开销
  4. 统一模板管理方式
  5. 合理使用模板助手
最后更新:2026-03-28