Знакомство с javascript-фреймворком Backbone я, как и многие, начинал с todo-туториала, на базе которого строилось дальнейшее использование фреймворка в своих проектах.
Но туториалы заканчиваются, и начинаются рабочие будни.
Думаю, многим знаком такой участок кода (из вышеупомянутого туториала):
Давайте разберемся подробнее:
Конечно, для небольших приложений вопросов нет. А если приложение большое и содержит десятки шаблонов?
Зачем загружать и инициализировать все шаблоны сразу? Нам может и не понадобиться большинство из них.
Хорошо бы подгружать шаблоны только тогда, когда они нам нужны и кешировать их.
Исходя из этого, напишем загрузчик:
Загружая шаблон по ajax, имеем дополнительную возможность кешировать его средствами браузера.
Очередь сделана для того, чтобы загрузчик не грузил шаблон для каждого вида модели из нашей коллекции, а сделал это один раз и передал готовый для работы шаблон в каждый вид.
Надеюсь, что комментариев к коду достаточно, чтобы понять, как все работает.
Далее нам необходимо научить наши views работе с шаблонами по-новому.
Напишем базовый класс вида, чтобы он умел работать с нашим загрузчиком шаблонов.
Таким образом, если у нас в параметрах вида передается templateUrl, то загружаем шаблон, иначе работаем по обычной логике.
Единственное отличие от стандартного поведения, функция непосредственного рендеринга выносится в метод _render. В принципе, ничто не мешает переписать код, оборачивающий все в метод render, но мне так показалось удобнее и проще.
Что на практике?
Вот так будет выглядеть измененная часть кода, показанного в начале статьи:
Готово!
Код загрузчика доступен здесь.
Пример изменненного todo-туториала можно посмотреть здесь.
Если интересно, что поменялось в todo: diff
Спасибо за внимание!
Но туториалы заканчиваются, и начинаются рабочие будни.
Думаю, многим знаком такой участок кода (из вышеупомянутого туториала):
window.AppView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: $("#todoapp"),
// Our template for the line of statistics at the bottom of the app.
statsTemplate: _.template($('#stats-template').html()),
...
Давайте разберемся подробнее:
- декларацию вида AppView необходимо производить после готовности DOM-дерева, то есть оборачивать в jQuery(function() {...}), что не всегда удобно.
- шаблон statsTemplate подгружается на этапе декларации вида, чтобы экземпляры могли использовать его без дополнительной загрузки и обработки
- шаблон находится внизу html-страницы и подгружается в DOM при загрузке страницы
Конечно, для небольших приложений вопросов нет. А если приложение большое и содержит десятки шаблонов?
Зачем загружать и инициализировать все шаблоны сразу? Нам может и не понадобиться большинство из них.
Загрузка шаблонов
Хорошо бы подгружать шаблоны только тогда, когда они нам нужны и кешировать их.
Исходя из этого, напишем загрузчик:
Core.Template = {
cache: {}, // здесь хранятся загруженные шаблоны
pending: {}, // очередь callback-ов, которые необходимо вызвать после загрузки шаблона
load: function(url, callback) {
callback = callback || function() {};
if (this.cache[url]) { //если шаблон уже загружен, просто вызовем callback
callback(this.cache[url]);
return;
}
// добавляем callback в очередь
if (this.pending[url]) {
this.pending[url].push(callback);
return;
}
this.pending[url] = [callback];
jQuery.ajax({ //загружаем шаблон
url : url,
dataType: 'text',
complete: function(resp) {
var cache =
this.cache[url] = _.template(resp.responseText); // парсим и заносим в кеш
_.each(this.pending[url], function(cb) { // обрабатываем очередь
cb(cache);
});
delete this.pending[url]; // очищаем очередь
}.bind(this),
error: function() {
throw new Error("Could not load " + url);
}
});
}
};
Загружая шаблон по ajax, имеем дополнительную возможность кешировать его средствами браузера.
Очередь сделана для того, чтобы загрузчик не грузил шаблон для каждого вида модели из нашей коллекции, а сделал это один раз и передал готовый для работы шаблон в каждый вид.
Надеюсь, что комментариев к коду достаточно, чтобы понять, как все работает.
Далее нам необходимо научить наши views работе с шаблонами по-новому.
Базовый класс вида для работы с шаблонами
Напишем базовый класс вида, чтобы он умел работать с нашим загрузчиком шаблонов.
Core.View = Backbone.View.extend({
renderQueue: false, // очередь запросов на рендеринг
initialize: function() {
if (this.templateUrl) {
Core.Template.load(this.templateUrl, function(data) {
this.template = data; // запоминаем шаблон
if (this.renderQueue !== false) { // обработаем очередь на рендер
_.each(this.renderQueue, function(item) {
this._render.apply(this, item);
}.bind(this));
this.renderQueue = false;
}
}.bind(this));
}
},
_render: function() {}, // эта функция будет переопределяться в видах вместо render
render: function() {
if (!this.template) { // если шаблон не загружен - дождемся загрузки и отрендерим после
this.renderQueue = this.renderQueue || [];
this.renderQueue.push(arguments);
return this;
}
return this._render.apply(this, arguments); //вызываем метод рендеринга
}
});
Таким образом, если у нас в параметрах вида передается templateUrl, то загружаем шаблон, иначе работаем по обычной логике.
Единственное отличие от стандартного поведения, функция непосредственного рендеринга выносится в метод _render. В принципе, ничто не мешает переписать код, оборачивающий все в метод render, но мне так показалось удобнее и проще.
Использование
Что на практике?
Вот так будет выглядеть измененная часть кода, показанного в начале статьи:
window.AppView = Core.View.extend({
el: $("#todoapp"),
// Загружаем шаблон отсюда
templateUrl: '/templates/app.html',
initialize: function() {
AppView.__super__.initialize.apply(this); // вызываем родительский инициализатор
...
},
// Переименовываем render в _render и statsTemplate в template
_render: function() {
this.$('#todo-stats').html(this.template({
total: Todos.length,
done: Todos.done().length,
remaining: Todos.remaining().length
}));
},
...
Готово!
Код загрузчика доступен здесь.
Пример изменненного todo-туториала можно посмотреть здесь.
Если интересно, что поменялось в todo: diff
Спасибо за внимание!