- PVSM.RU - https://www.pvsm.ru -

Catberry.js — это фреймворк для разработки изоморфных JavaScript-приложений на node.js с использованием модульной архитектуры и быстрых механизмов рендеринга. Этот фреймворк позволяет написать модуль приложения один раз и использовать его как на сервере для рендеринга страниц для поисковых роботов, так и в браузере для одностраничного приложения, запрашивая только данные для шаблонов.
Впервые термин изоморфные приложения лично я увидел в блоге компании airbnb [1], перевод этой статьи можно прочитать на Хабре [2], хотя этот термин начал звучать немного раньше, например в блоге nodejitsu [3]. Поэтому немного сложно сказать, кто это придумал, но факт в том, что в наше время существует целый класс веб-приложений, которые принято называть изоморфными. На Хабре этот термин в основном упоминался в статьях о фреймворках от gritzko [4] и zag2art [5].
Увидев это странное название, многие разработчики пугаются и стараются избегать знакомство с ним. И очень зря, я считаю, что именно изоморфные приложения — это будущее (а может быть уже и настоящее) веб-приложений.
Как бы страшно оно не звучало, на самом деле всё достаточно просто — это приложения, которые могут переиспользовать серверный код в браузере и вести себя одинаково как на сервере, так и в браузере. Другими словами, вы пишете один раз код вашего приложения и получаете серверный бэк-енд, который рендерит страницы для поисковых роботов и отзывчивое одностраничное приложение в браузере. Оно, в свою очередь, может и вовсе не грузить больше ни байта HTML с сервера, а запрашивать только данные для рендеринга в браузере. Практически мечта, не правда ли?
Возникновение класса изоморфных приложений обосновано желаниями разработчиков, например:
Лично я все это хочу столько, сколько занимаюсь веб-разработкой, и, очевидно, не я один, так как в мире радуги, единорогов и изоморфизма уже есть множество фреймворков для разработки изоморфных приложений:
Ещё есть такой подход как MEAN [11] (MongoDB+Express+Angular+node.js), который делает Angular-приложения изоморфными.
Когда я хотел начать очередной веб-проект, я стал изучать все существующие на тот момент решения и увидел ряд недостатков:
Хочется иметь возможность разрабатывать изоморфные и просто сайты, чтобы они работали быстро и просто. И так как мои поиски завершились неудачей, я понял, что можно разработать фреймворк, который заполнит эту нишу хотя бы для меня.
Называется этот фреймворк Catberry.js, и сейчас он уже в версии 3.0. Не то чтобы он стар, но для версионирования используется Semantic Versioning 2.0.0 [13], а фреймворк пару раз претерпел изменения без обратной совместимости. Catberry достаточно лёгкий фреймворк, со своей идеологией, которая гласит «Хранение и обработка данных не должна быть частью приложения Catberry, это должен делать отдельный RESTful сервис».
Знакомство с фреймворком стоит начать с подхода SMP (Service-Module-Placeholder), который заменяет привычное многим MVC (Model-View-Controller), но, обещаю, будет выглядеть знакомо. Опять же, стоит оговориться, я не против MVC, он очень даже хорош, но для определенного класса приложений, где как раз необходимо обновление данных в реальном времени. Тот MVC, который используется сейчас в разработке веб-приложений, часто оказывается не тем, что изначально было придумано в качестве MVC, а как некая интерпретация. Так вот свою интерпретацию я решил назвать иначе, чтобы не путать коллег.
Как видно из названия, приложение строится на трёх компонентах.
Service — внешний компонент, представляющий из себя RESTful сервис, к которому постоянно обращается наше Catberry-приложение. Этот сервис может быть реализован на абсолютно любой платформе: Erlang, Go, PHP, .NET, что угодно. Вы ограничены только протоколом HTTP 1.1.
Модуль в понятии Catberry-приложения это набор логики, которая подготавливает данные для шаблонизатора и обрабатывает события от пользователя. Другими словами, если нужно отрендерить часть страницы, Catberry находит ответственный за эту часть страницы модуль и просит его подготовить контекст данных для шаблона, учитывая текущее состояние всего приложения (например, параметры в текущем URL).
С первого взгляда можно сказать, что это просто шаблон. На самом деле он может иметь HTML-элементы, которые ссылаются на другие плейсхолдеры, причём такие ссылки могут появляться динамически во время рендеринга, и это вставит другой плейсхолдер внутрь текущего. Например, в содержимое элемента плейсхолдер «paw» модуля «cat». Почему используется id, спросите вы? А для того, чтобы плейсхолдеры можно было очень быстро найти в DOM, когда рендеринг работает в браузере. Это действительно очень экономит время.
Как было упомянуто, плейсхолдер связан с модулем, а точнее модуль владеет неким набором плейсхолдеров, и никому больше эти плейсхолдеры принадлежать не могут. Отсюда может возникнуть вопрос: «А как же разбить приложение на плейсхолдеры и модули?». Есть два достаточно простых правила:
Наверное, кто-то из читателей обязательно подумает: «А в чём же отличие от MVC?». Если постараться, можно сказать, что Service==Model, Controller==Module и View=Placeholder. Но это как раз то, о чём я говорил ранее, термин MVC в наше время очень искажён, и когда люди интерпретируют его по-своему, они называют это MVC. Я же счёл нужным указать другое название, потому что:
Catberry-приложение работает именно так, как я ранее описывал изоморфные приложения:
Для большего понимания картинка

Как можно понять из описания и схемы, когда происходит рендеринг на сервере, для отрисовки каждого плейсхолдера обычно надо сделать запрос на RESTful-сервис. Это может занять значительное для пользователя время. Чтобы пользователь не разглядывал пустую страницу с крутящимся лоадером, пока мы делаем запросы для всех плейсхолдеров, был разработан потоковый (stream-based) движок отрисовки. Его задача — доставлять в браузер страницу по мере готовности. Другими словами, как только запрос к сервису для отрисовки заголовка страницы выполнен, пользователь тут же увидит этот заголовок у себя в браузере, не дожидаясь готовности всей страницы.
Я сам очень сильно не люблю, когда при открытии страницы несколько секунд вижу белый экран и даже не знаю, дошёл ли мой запрос до бэк-енда, или я сейчас увижу «504 Gateway Timeout». Обычно я закрываю такие сайты через 3—4 секунды белого экрана.
С потоковым рендерингом я сразу же увижу отклик, и то, что сайт для меня работает, старается и пыхтит, собирая данные для отрисовки. Ещё один приятный момент, что стриминг не буферизирует данные в большом объеме, что будет хорошо экономить память нашего сервера с приложением. Ну и самый приятный момент, то, что браузер, получив HEAD-элемент (который приходит с сервера почти сразу) начинает парсить JavaScript и CSS, а также загружать все указанные в странице ресурсы, всё это будет работать параллельно, как на диаграмме ниже.

Потоковый рендеринг это, конечно, хорошо, но только когда мы ограничены последовательной загрузкой страницы по потоку TCP-соединения. Когда у нас уже есть готовая страница в браузере, и пользователь кликает по ссылке, нам нужно перестроить часть страницы, под новое состояние приложения, здесь мы уже ничем не ограничены. Можем выполнять запросы к RESTful сервису параллельно, а по результатам тут же обновлять плейсхолдеры. А если внутри есть ссылки на другие, то снова параллельно запрашивать для них данные. Таким образом получается невероятно быстрый рендеринг плейсхолдеров в браузере. К тому же, если один из запросов будет получать ответ очень долго, то это не повлияет на рендеринг остальных плейсхолдеров.
Когда мы разрабатываем веб-приложение, нам часто нужно воспользоваться действиями, которые зависят от реализации в определенной среде, то есть работают по-разному в браузере и на сервере. Для это в Catberry есть изоморфные реализации таких действий. Они внешне работают идентично, имеют одинаковый программный интерфейс, но внутри реализованы, используя средства текущей среды. Вот перечень таких реализаций:
Именно это API даёт изоморфность приложения на Catberry.js.
Архитектура фреймворка построена на реализации паттернов Service Locator [15] и Dependency Injection [16].
Например,
var cat = catberry.create(config); // создаётся экземпляр приложения
cat.locator.register('uhr', UHR); // можно регистрировать конструкторы по имени
cat.locator.registerInstance('uhr', new UHR()); // или сразу экземпляры
cat.locator.resolve('uhr'); // получить экземпляр
cat.locator.resolveAll('uhr'); // получить все экземпляры под таким именем
cat.locator.resolveInstance(SomeConstructorDependsOnUHR); // создать экземпляр с внедрением зависимостей
//Зависимости внедряются достаточно просто:
function ModuleConstructor ($uhr, someConfigSection) {
// можно использовать зависимость $uhr
// и даже секцию конфига someConfigSection
}
Такие внедрения зависимостей не ломаются при минификации, так как она делается самим фреймворком с использованием UglifyJS2 [17].
Каждый модуль — это директория с файлом index.js, который должен экспортировать конструктор модуля (модуль — это конструктор с объявленным прототипом). Также у модуля может быть директория placeholders, в которой располагаются шаблоны-плейсхолдеры модуля.
Каждый модуль может реализовывать три группы методов: render, handle и submit. Тут используется конвенция именования, если ваш плейсхолдер называется some-awesome-placeholder, то вы должны реализовать метод renderSomeAwesomePlaceholder, если хотите подготовить данные для него. Можете и не реализовывать, ничего от этого не сломается, а шаблон отрендериться с пустым контекстом, что тоже вполне допустимо. Такая конвенция применяется и к handle/submit методам, которые обрабатывают события со страницы.
Пример реализации всех трёх методов:
ModuleConstructor.prototype.renderSome = function () {
// получение данных
return {some: data}; // или Promise
};
ModuleConstructor.prototype.handleSome = function (event) {
// как-то обрабатываем событие
// event.args
// event.element
// event.isEnding
// event.isHashChanging
// можно вернуть Promise
};
ModuleConstructor.prototype.submitSome = function (event) {
// отправляем данные формы
// event.values
// event.element
};
Иногда необходимо выполнить привязку к элементам DOM после того, как плейсхолдер будет отрендерин, для этого предусмотрены after methods, например для метода renderSome выше:
ModuleConstructor.prototype.afterRenderSome = function (dataContext) {
// можно делать что угодно с отрендеренным плейсхолдером
};
Можно добавить такие методы также для handle и submit методов.
Пример реализации модуля можно посмотреть на Гитхабе [18].
Как уже упоминалось в примерах, везде, где используются асинхронные вызовы, в Catberry используются Promises [19] (недавно была отличная статья [20]). Причём если таковые уже есть в браузере [21], будет использоваться нативная реализация, иначе — библиотека-полифил [22] от одного из авторов спецификации. Тип Promise, при этом, доступен глобально, ничего подключать не нужно, как будто вы работаете с нативными примостим.
Сейчас на основе фреймворка уже запущен сайт проекта Конфеттин [23], где можно ощутить производительность и отзывчивость приложения на основе Catberry. К тому же, во всю идет разработка следующей версии Flamp [24], которая в обозримом будущем уже увидит свет. Чего я лично жду с нетерпением.
Если это, довольно беглое, описание фреймворка вас заинтересовало, то можно начать с этих двух строк в терминале:
npm -g install catberry-cli
catberry init example
Таким образом, вы получите код рабочего примера, который работает с GitHub API и инструкции как его запустить. В этом примере продемонстрированы типовые риализации вещей, которые часто приходится делать в веб-приложении. Этой же CLI-утилитой [25] можно делать ещё много чего интересного. Например, создать новый проект или добавить в проект модуль.
Если устанавливать на свой компьютер ничего не хочется или нет такой возможности, есть этот же готовый проект на Runnable [26], но там можно превысить лимит запросов к GitHub API.
Подробную документацию и примеры можно найти на GitHub [27].
Ну и, конечно, сама страница репозитория [28] на GitHub и Twitter-аккаунт catberryjs [29], в котором всегда самые свежие новости о фреймворке.
Автор: pragmadash
Источник [30]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/74196
Ссылки в тексте:
[1] блоге компании airbnb: http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/
[2] прочитать на Хабре: http://habrahabr.ru/post/203444/
[3] блоге nodejitsu: http://blog.nodejitsu.com/scaling-isomorphic-javascript-code/
[4] gritzko: http://habrahabr.ru/company/swarm/blog/238785/
[5] zag2art: http://habrahabr.ru/post/206526/
[6] Rendr: https://github.com/rendrjs/rendr
[7] Kraken: http://krakenjs.com/
[8] Mojito: https://github.com/yahoo/mojito
[9] Meteor: https://www.meteor.com/
[10] Derby: http://derbyjs.com/
[11] MEAN: http://mean.io
[12] SCADA: http://ru.wikipedia.org/wiki/SCADA
[13] Semantic Versioning 2.0.0: http://semver.org
[14] описанию MVC: https://ru.wikipedia.org/wiki/Model-View-Controller
[15] Service Locator: http://en.wikipedia.org/wiki/Service_locator_pattern
[16] Dependency Injection: http://en.wikipedia.org/wiki/Dependency_injection
[17] UglifyJS2: https://github.com/mishoo/UglifyJS2
[18] Гитхабе: https://github.com/catberry/catberry-cli/blob/master/templates/example/catberry_modules/search/SearchModule.js
[19] Promises: https://www.promisejs.org
[20] статья: http://habrahabr.ru/post/242767/
[21] уже есть в браузере: http://caniuse.com/#search=promises
[22] библиотека-полифил: https://github.com/then/promise
[23] Конфеттин: http://konfettin.ru
[24] Flamp: http://flamp.ru
[25] CLI-утилитой: https://github.com/catberry/catberry-cli
[26] Runnable: http://runnable.com/U_RHWYh4VjkgAeCl/example-catberry-js-application-that-works-with-github-api
[27] найти на GitHub : https://github.com/catberry/catberry/blob/master/docs/index.md
[28] страница репозитория: https://github.com/catberry/catberry
[29] catberryjs: https://twitter.com/catberryjs
[30] Источник: http://habrahabr.ru/post/242909/
Нажмите здесь для печати.