Заметки за 2013 год (страница 5)

Плагин Backbone.Native

У Backbone среди зависимостей есть жесткая привязка jQuery. С помощью неё обеспечивается манипуляция с DOM-элементами, XHR-запросы и делегация событий. Всё это было реализовано в компактном плагине Backbone.Native.

Комментарии к заметке: 3

Кеширование медленных вычислений

Когда функция неоднократно вызывается с одними и теми же параметрами, и каждый раз возвращает для этих параметров одинаковый результат, то можно сохранить его и сразу возвращать без долгих вычислений.

В библиотеке 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 я применяю для кеширования асинхронных запросов.

Комментарии к заметке: 3

Коллекция разнотипных объектов в 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
});

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

Практическое применение паттерна можно посмотреть в исходном коде примера.

Комментарии к заметке: 15

Ограничение скорости загрузки по сети

Мне для тестирования бывает нужно ограничить скорость 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: С подачи пользователей добавил фильтр ресурсов, для которых нужно применять ограничение скорости, и фильтр, который пропускает ресурсы без ограничений.

Комментарии к заметке: 4

Разметка «хлебных крошек» с помощью 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> &#8250;
    <span itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
      <a href="http://noteskeeper.ru/topic/markup/" itemprop="url">
        <span itemprop="title">Вёрстка</span>
      </a> &#8250;
      <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>

Пример в выдаче Google

В примере видно, что под ссылкой на целевую страницу есть ещё две ссылки: на корень сайта, и на страницу категории статьи. Таких промежуточных ссылок может быть больше.

Формат разметки, возможно, ещё будет меняться. На текущий момент, все объекты типа 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 ». Поисковик прекрасно воспринимает контент, скрытый такой техникой.

Комментарии к заметке: 23