Интеграция с внешними шаблонизаторами

Backbone.js изначально предоставляет простую систему для связывания данных с представлением через метод render в представлениях (View), однако встроенные возможности шаблонизации ограничены. Для создания масштабируемых и поддерживаемых интерфейсов часто используют внешние шаблонизаторы, такие как Underscore.js templates, Handlebars, Mustache, EJS и другие. Интеграция внешнего шаблонизатора позволяет разделять логику приложения и оформление, повышая читаемость кода и облегчая поддержку.


Использование Underscore.js templates

Backbone.js тесно интегрирован с библиотекой Underscore.js, поэтому её шаблонизатор является самым простым вариантом. Шаблон создаётся как строка HTML с плейсхолдерами вида <%= property %>:

var ItemView = Backbone.View.extend({
    tagName: 'li',
    template: _.template('<strong><%= name %></strong> – <%= price %> руб.'),
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

var item = new Backbone.Model({ name: 'Книга', price: 450 });
var view = new ItemView({ model: item });
$('#items').append(view.render().el);

Ключевые моменты:

  • Метод _.template компилирует шаблон в функцию, принимающую объект данных.
  • Использование model.toJSON() позволяет передавать данные модели в шаблон в виде простого объекта.
  • render() отвечает за обновление DOM-элемента представления.

Интеграция с Handlebars

Handlebars предоставляет более мощный синтаксис и поддержку хелперов, что полезно для сложной логики отображения:

var ItemView = Backbone.View.extend({
    tagName: 'li',
    
    template: Handlebars.compile(
        '<strong>{{name}}</strong> – {{price}} руб.'
    ),
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

var item = new Backbone.Model({ name: 'Ручка', price: 120 });
var view = new ItemView({ model: item });
$('#items').append(view.render().el);

Особенности интеграции:

  • Handlebars.compile() возвращает функцию шаблона, аналогично Underscore.
  • Можно создавать кастомные хелперы для форматирования данных прямо в шаблоне.
  • Поддержка вложенных объектов и условной логики ({{#if}}, {{#each}}) упрощает работу с коллекциями.

Использование Mustache

Mustache реализует логический минимализм, что делает шаблоны полностью декларативными:

var ItemView = Backbone.View.extend({
    tagName: 'li',
    
    template: function(data) {
        return Mustache.render('<strong>{{name}}</strong> – {{price}} руб.', data);
    },
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

var item = new Backbone.Model({ name: 'Блокнот', price: 250 });
var view = new ItemView({ model: item });
$('#items').append(view.render().el);

Особенности:

  • Логика шаблона минимальна, что способствует поддерживаемости.
  • Для условий и циклов используются секции ({{#section}}…{{/section}}).
  • Отлично подходит для передачи данных с сервера в чистом JSON.

Организация шаблонов

Для больших проектов рекомендуется хранить шаблоны отдельно в HTML-файлах или в скриптах с type="text/template":

<script id="item-template" type="text/template">
    <strong><%= name %></strong> – <%= price %> руб.
</script>

И использовать в представлении:

var ItemView = Backbone.View.extend({
    tagName: 'li',
    template: _.template($('#item-template').html()),
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

Преимущества такого подхода:

  • Шаблоны отделены от JavaScript-кода.
  • Удобно редактировать HTML без пересборки скриптов.
  • Позволяет подключать шаблоны динамически.

Рендеринг коллекций

Для отображения коллекций Backbone.js применяется комбинация коллекций (Backbone.Collection) и представлений элементов:

var ListView = Backbone.View.extend({
    tagName: 'ul',
    
    initialize: function() {
        this.listenTo(this.collection, 'add', this.addOne);
    },
    
    render: function() {
        this.collection.each(this.addOne, this);
        return this;
    },
    
    addOne: function(item) {
        var itemView = new ItemView({ model: item });
        this.$el.append(itemView.render().el);
    }
});

var items = new Backbone.Collection([
    { name: 'Карандаш', price: 50 },
    { name: 'Линейка', price: 30 }
]);

var listView = new ListView({ collection: items });
$('#items').append(listView.render().el);

Особенности работы с коллекциями:

  • Событие add позволяет автоматически обновлять интерфейс при добавлении модели.
  • Каждая модель рендерится отдельным представлением, что сохраняет принцип единичной ответственности.
  • Легко интегрировать любой внешний шаблонизатор для отдельного элемента.

Рекомендации по производительности

  1. Компиляция шаблонов один раз: создание функции шаблона при каждом рендере снижает производительность.
  2. Использование documentFragment для массовой вставки элементов в коллекциях.
  3. Кэширование шаблонов: хранение предварительно скомпилированных шаблонов уменьшает задержки при рендере.
  4. Минимизация логики в шаблонах: все вычисления и форматирование данных лучше выполнять в модели или в хелперах.

Поддержка сторонних шаблонизаторов

Backbone.js не ограничен встроенными методами и может интегрироваться с любыми современными шаблонизаторами, включая Pug, Nunjucks и Liquid, при условии, что шаблон возвращает готовую HTML-строку для вставки в this.$el.html(). Главная задача — унификация интерфейса вызова: компиляция шаблона + передача модели через toJSON() + вставка результата в DOM.

Такой подход обеспечивает гибкость при выборе шаблонизатора и позволяет создавать масштабируемые приложения с чистой архитектурой.