Когда функция неоднократно вызывается с одними и теми же параметрами, и каждый раз возвращает для этих параметров одинаковый результат, то можно сохранить его и сразу возвращать без долгих вычислений.
В библиотеке Underscore.js есть метод, который для любой функции делает версию этой функции с кешированием — memoize.
Так, например, её можно применять для кеширования _.template()
.
var View = Backbone.View.extend({
render: function () {
var markup = _.template(this.template, this.model.toJSON());
this.$el.html(markup);
}
});
Вместо того, чтобы при каждом обновлении данных компилировать тектовый шаблон , он будет преобразован в функцию только при первом использовании. А все последующие вызовы будут использовать уже скомпилированный вариант.
var cachedTemplate = _.memoize(_template);
var View = Backbone.View.extend({
render: function () {
var markup = cachedTemplate(this.template, this.model.toJSON());
this.$el.html(markup);
}
});
Разница в производительности при рендеринге простенького шаблона и его кешированного варианта налицо.
Все скомпилированные шаблоны находятся в хранилище ключ-значение внутри замыкания
_.memoize()
. Ключом в нашем случае выступает текст шаблона, а значением — скомпилированная функция.
В качестве ключа может быть не только текстовая строка. Если первый параметр кешируемой функции не является строкой, то ключом станет то, что венёт его метод .toString()
. В некоторых случаях это может оказаться неприемлемо (например, когда первым аргументом идёт объект). Чтобы избежать коллизий, в _.memoize()
можно передать опциональную функцию, генерирующую ключ из аргументов.
var cachedTemplate = _.memoize(_.template, function (value) {
return JSON.stringify(value);
});
Аналогичный приём, но без использования Underscore.js я применяю для кеширования асинхронных запросов.
Коментарии к заметке
Частный пример запоминания темплейтов можно улучшить, передавая в memoize скомпилированный в функцию шаблон, таким образом кешируя непосредственно результаты рендеринга.
http://jsperf.com/underscore-template-caching/2
Да, Павел, ты совершенно прав. Только в твоём бенчмарке как раз ошибка сериализации объекта — для любого содержимого мы получаем одну и ту же строку
Вот пример, подтвержающий это: http://jsfiddle.net/mistakster/Cd2Q3/
В общем случае объекты можно сериализовать в JSON. Если же о нём изначально что-то известно, то можно составить ключ вручную.
Бенчмарк с исправленой сериализацией — http://jsperf.com/underscore-template-caching/3
Но, я хотел бы предостеречь от такого агрессивного кеширования отрендерённых шаблонов в одностраничном приложении. Каждая новая комбинация параметров будет порождать элемент в кеше, который никогда не очистится. Чтобы избежать чудовищного расхода памяти, нужно городить менеджер, управляющий временем жизни элементов. А лучше сразу делать байндинг DOM-элементов и соответствующих полей модели.
Точно, спасибо!
Конечно, кешировать нужно с оглядкой на то, как кеш будет использоваться. Иногда это может и навредить.