У Backbone среди зависимостей есть жесткая привязка jQuery. С помощью неё обеспечивается манипуляция с DOM-элементами, XHR-запросы и делегация событий. Всё это было реализовано в компактном плагине Backbone.Native.
Заметки за 2013 год (страница 5)
Кеширование медленных вычислений
Когда функция неоднократно вызывается с одними и теми же параметрами, и каждый раз возвращает для этих параметров одинаковый результат, то можно сохранить его и сразу возвращать без долгих вычислений.
В библиотеке 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 я применяю для кеширования асинхронных запросов.
Коллекция разнотипных объектов в Backbone.js
В коллекциях Backbone.js не обязательно должны храниться объекты одного типа. Если в неё явно добавить экземпляр класса, унаследованного от Backbone.Model
, то он без изменений будет помещён в неё. У коллекции есть атрибут model
, в котором хранится конструктор модели. Когда в коллекцию добавляется простой объект, то он в качестве параметра передаётся в этот конструктор.
В атрибуте
model
можно, фактически, указать любую функцию. Возвращаемые значения (экземпляры классов, унаследованных от Backbone.Model
) будут помещаться в коллекцию. Такие функции ещё называют фабриками или фабричными методами. В документации к атрибуту model
как раз представлен вариант такой фабрики.
Чтобы каждый раз не писать однотипных проверок в подобных ситуациях, я предлагаю пользоваться паттерном «Фабрика фабрик»:
App.Factory = function (getter, hash, def) {
return function () {
var value = getter.apply(this, arguments);
var ctor = hash[value] ? hash[value] : def;
return new ctor(arguments[0], arguments[1]);
};
};
- getter — функция, которая возвращает значение, на основании которого будет выбираться требуемый конструктор;
- hash — объект ключ-значение с перечислением конструкторов, которые могут использоваться фабрикой;
- def — конструктор по-умолчанию.
Предположим, что в коллекции могут находиться объекты следующих классов:
App.Item = Backbone.Model.extend({});
App.ItemNews = App.Item.extend({});
App.ItemAction = App.Item.extend({});
App.ItemMessage = App.Item.extend({});
Таким образом, фабрика объектов будет выглядеть так:
App.ItemFactory = App.Factory(
function (attr) { return attr.type; },
{
"news": App.ItemNews,
"action": App.ItemAction,
"message": App.ItemMessage
},
App.Item
);
App.Factory
возвращает функцию, которая, в свою очередь, будет возвращать требуемый экземпляр в зависимости от значения атрибута type
. Эту функцию и передаём в описание коллекции.
App.Items = Backbone.Collection.extend({
model: App.ItemFactory
});
Теперь коллекция будет заполняться моделями нужного типа автоматически.
Практическое применение паттерна можно посмотреть в исходном коде примера.
Ограничение скорости загрузки по сети
Мне для тестирования бывает нужно ограничить скорость HTTP-трафика для проверки того, как ресурс работает в тех или иных условиях.
Для этих целей я написал на Node.js прокси-сервер, который выполняет одну простую задачу: пропускает через себя HTTP-трафик, лимитируя скорость до указанного значения.
Устанавливается он в систему с помощью npm:
npm install -g throttle-proxy
И запускается командой:
throttle-proxy
По-умолчанию, сервер будет слушать подключения на порту 3128 и ограничивать скорость до 100 тысяч байт в секунду. Эти значения можно поменять с помощью соответствующих параметров при запуске сервера.
throttle-proxy --speed 50000 --port 9999
Остановить прокси-сервер можно комбинацией клавиш Ctrl+C
в консоли, где он запущен.
Не забудьте только указать адрес 127.0.0.1
и порт 3128
(или выбранный вами порт) в настройках системы в качестве HTTP-прокси сервера.
Исходный код доступен на GitHub — throttle-proxy
Update: С подачи пользователей добавил фильтр ресурсов, для которых нужно применять ограничение скорости, и фильтр, который пропускает ресурсы без ограничений.
Разметка «хлебных крошек» с помощью microdata
«Хлебные крошки» (breadcrumbs) помогают пользователям ориентироваться в структуре сайта. Хотя, стоит заметить, что в современном дизайне их встречаешь всё реже и реже. Но сейчас они могут оказаться полезными не только пользователям, но и поисковикам.
Поисковики и сами прекрасно умеют определять структуру сайта. Google, например, может построить «крошки» от главной страницы сайта до целевой страницы из цепочки ссылок и вывести их в сниппете. Но, лучше всего «хлебные крошки» разметить с помощью microdata. В словаре Data-Vocabulary.org есть специальный тип для этого — Breadcrumb .
<div class="breadcrumbs">
<span itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="http://noteskeeper.ru" itemprop="url">
<span itemprop="title">Свежие заметки</span>
</a> ›
<span itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="http://noteskeeper.ru/topic/markup/" itemprop="url">
<span itemprop="title">Вёрстка</span>
</a> ›
<span itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="http://noteskeeper.ru/527/" itemprop="url">
<span itemprop="title">Вёрстка независимыми блоками</span>
</a>
</span>
</span>
</span>
</div>
В примере видно, что под ссылкой на целевую страницу есть ещё две ссылки: на корень сайта, и на страницу категории статьи. Таких промежуточных ссылок может быть больше.
Формат разметки, возможно, ещё будет меняться. На текущий момент, все объекты типа Breadcrumb
неявно связываются между собой. Последовательность в цепочке определяется их последовательностью в документе. Для явного связывания объектов есть поле child
. В этом случае объекты должны быть вложены друг в друга (как в моём примере).
Казалось бы, с разметкой хлебных крошек в виде неупорядоченного списка можно было распрощаться. Однако я нашёл способ, как создать требуемую связанную структуру. На помощь приходит свойство itemref.
<ul class="breadcrumbs">
<li itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
itemref="breadcrumb-1">
<a href="http://noteskeeper.ru" itemprop="url">
<span itemprop="title">Свежие заметки</span>
</a>
</li >
<li itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
id="breadcrumb-1" itemref="breadcrumb-2">
<a href="http://noteskeeper.ru/topic/markup/" itemprop="url">
<span itemprop="title">Вёрстка</span>
</a>
</li>
<li itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
id="breadcrumb-2">
<a href="http://noteskeeper.ru/527/" itemprop="url">
<span itemprop="title">Вёрстка независимыми блоками</span>
</a>
</li>
</ul>
Технически, получилась точно такая же структура — чётко связанный список «крошек». Осталось только проверить на практике, как к нему отнесётся Google.
Обновление: Гугл нормально связал между собой крошки, через аттрибут itemref
.
Ну, и напоследок напомню, что если «хлебные крошки» всё же не желательны в дизайне страницы, то их можно спрятать классом «visuallyhidden ». Поисковик прекрасно воспринимает контент, скрытый такой техникой.