Permalink
Fetching contributors…
Cannot retrieve contributors at this time
143 lines (83 sloc) 16.8 KB

Slot - изоморфный js-фрейворк для масштабируемых web-приложений.

Концепция

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

Изоморфность

При создании web-приложений, особенно одностраничных, часто возникает проблема дублирования кода: клиентский js-код приходится дублировать серверным кодом. Изоморфные фреймворки, такие как Slot, решают эту проблему, стирая грани между front-end и back-end разработкой - один и тот же код работает как на сервере, так и на клиенте. Это позволяет, например, делать одностраничное веб-приложение, которое “из коробки” может быть полноценно проиндексировано поисковыми машинами.

Рассмотрим изоморфность более подробно на примере.

Допустим, вы зашли на главную страницу одностраничного новостного портала по адресу

example.com/

На этом портале есть список анонсов новостей. При клике на анонс новости номер 168, эта новость разворачивается: появляется её полный текст. С точки зрения кода меняется состояние приложения (в частности DOM-дерево) (подробнее о состоянии см. ниже) так, чтобы на экране появилась выбранная новость. При этом новое состояние попадает в url при помощи History API браузера, и в адресной строке пользователя уже будет:

example.com/news/168

Важно отметить, что браузер не делает запрос по этому url, он просто приводит его в соответствие с состоянием приложения, в котором теперь развёрнута новость за номером 168. Если посмотреть в отладчике html-код страницы, то в нём будет код этой новости, хотя в исходном ответе сервера его не было: он был сгенерирован браузерным js-кодом после смены состояния приложения, и вставлен в DOM-дерево. Эта смена состояния инициирована действием пользователя.

Если теперь в новой вкладке перейти по этому url (example.com/news/168), то с сервера придёт тот же самый html код, который есть в предыдущей вкладке: разница между ними только в том, что, в первом случае html получился в результате модификации первоначального html-кода, а во втором случае он целиком пришёл с сервера.

Пути достижения результата разные, но сам результат один. Не важно, каким путем пользователь его достиг- кликами в браузере или переходом по прямой ссылке; важно то, что одному и тому же состоянию строго соответствует одно и то же представление.

Раньше эти два пути реализовывали два разных разработчика: front-end и back-end. Чаще это были даже разные языки программирования (например js и php). В изоморфных фреймворках одинаковый html-код генерируется клиентом или сервером одним и тем же js-кодом. Именно эта особенность позволяет назвать фреймворк, в частности Slot, изоморфным.

Состояние

Состояние - это максимально краткое описание представления приложения. В Slot состояние представлено js-объектом. Из этого объекта однозначным образом восстанавливается html-код. Изменять этот объект могут действия пользователя или, например, переходы по истории браузера. Важно, что состояние управляет приложением, а не наоборот.

В других фреймворках (например Ember) часто под состоянием понимают url; в случае со Slot это не так - состояние реализовано абстрактно и может работать с url, только с его хэшбэнгом, или вообще без него. Есть и другие существенные отличия, о которых речь пойдёт ниже.

Разберём пример с новостным порталом с точки зрения состояния. Для простоты будем считать, что состояние синхронизированно с url при помощи History API. Начальное состояние будет описываться пустым объектом:

state1 = {}

Этому состоянию соответствует пустой url. А состояние выбранной новости, будет описываться, например, таким объектом:

state2 = {
    news: 168
}

и ему соответствует “/news/168”.

При любом изменении, новое значение состояния записывается в History браузера, и может быть полностью восстановлено при движении по истории браузера кнопками “назад” и “вперёд”. То есть, если в рассматриваемом примере, находясь в state2 пользователь нажмёт кнопку “назад”, History API скажет трекеру состояния приложения о том, что состояние изменилось, и новое состояние - это state1. Синхронно с этим вернётся и url.

Приложение реагирует на смену состояния, а не на причину, которая привела к этому изменению. Иными словами, если пользователь находится в state1 и нажимает “вперёд” - это запустит ровно тот же управляющий код, как если бы он кликнул по кнопке “раскрыть новость” в интерфейсе приложения. Смену состояния приложения обрабатывает один и тот же код, независимо от причины этой смены.

Состояние может быть надмножеством url.

Вернёмся к раскрытой новости, то есть к состоянию state2. Допустим, у новости есть комментарии, и находясь на странице конкретной новости, можно раскрыть какой-то конкретный комментарий. Раскрытие первого комментария приведёт к изменению состояния приложения на:

state3 = {
    news: 168,
    comment: 1
}

Slot позволяет сделать так, чтобы раскрытие комментария записывалось в историю, но не попадало в url. В этом случае url останется прежним:

example.com/news/168

Однако движение по истории кнопкой “назад” (state3 -> state2 -> state1) будет доступно пользователю. Это позволяет делать более тонкую seo-оптимизацию без ущерба для UX.

Состояние в Slot реализовано абстрактно.

Состояние можно связать с историей и url, полностью или частично, но способ и конкретная реализация этой связи не имеют существенного значения. Например, если у браузера нет поддержки History API, переход может быть связан с хэшбэнгом (url с якорем типа /#!news/168), либо вообще не использовать url . В последнем случае, правда, перестанут работать нативные браузерные кнопки “назад” и “вперёд”.

Архитектура

Веб-приложение, построенное на слоте, состоит из модулей. Модули всегда имеют строгую иерархическую структуру: у каждого модуля есть 1 родительский модуль, и неотрицательное число дочерних. Исключение из правил - главный модуль, у которого родителя нет.

Модуль

Кирпичик, из которых состоит веб-приложение на Slot. Минимально модуль состоит из 1 js-файла, который определяет его интерфейс и внутреннюю логику. Однако, с модулем могут быть ассоциированы html-шаблоны, css и картинки. С точки зрения пользователя, модуль это всегда некий виджет, прямоугольная область приложения. С точки зрения DOM-дерева, это нода, логически связанные с ней дочерние ноды и логика.

Модуль имеет следующие поля:

init. Изоморфный метод, вызываемый при инициализации модуля.

bind. Клиентский метод, выполняемый при навешивании событий на html-элементы модуля.

viewContext. Изоморфный метод, возвращающий объект-контекст главного шаблона. В зависимости от реализации, объект может быть моделью.

elements. Декларативное описание элементов модулей, и ассоциированных с этими элементами обработчиков событий.

interface. Методы модуля. dispatcher. Диспетчер модуля, который отлавливает события от дочерних модулей.

slot

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

Строгая иерархическая структура модулей приводит к тому, что во время общения между собой модули однозначно делятся на тех, кто уведомляет (notify) о каких-то событиях, и тех, кто приказывает (broadcast) сделать что-то конкретное. Оба метода имеются в интерфейсе объекта slot.

init. Создание нового дочернего модуля.

mod. Без аргументов работает как геттер объекта модификаторов. При передаче объекта в качестве аргумента выставляет модификаторы по каждой паре ключ-значение переданного объекта.

notify. Единственный способ общения с неизвестным для модуля “внешним миром”. Генерирует сообщение, которое будет проброшено вертикально вверх по цепочке родительских модулей.

broadcast. Генерирует сообщение вертикально вниз модулям-потомкам (по-умолчанию - всем).

БЭМ

Слот не привязан ни к какой методологии верстки: писать html и css код можно как угодно, без ограничений. Однако, в слоте есть целый ряд вспомогательных систем, объединяющих js- и css-код в контексте методологии БЭМ. Использование методологии БЭМ упрощает разработку приложения на Slot.

Плагин

В объект slot можно добавлять свои методы.

Компонент

Логическая единица кода, используемая одним или несколькими модулями, не имеющая представления в виде html / css кода. От обычных вспомогательных библиотек, компонент отличается тем, что он может зависеть от app и / или других компонентов.

app

Главный объект, связывающий модули, компоненты и прочий код в единое приложение. Не доступен внутри модулей.

Отметим некоторые ключевые моменты жизни app:

  1. Создание инстанса app. Подключается конструктор slot/app.js и создаётся инстанс приложения - app.
  2. Инициализация. На этом этапе читаются все конфиги, выбирается главный модуль при помощи ключа конфига mainModule, который может быть функцией.
  3. Инициализация модулей. Асинхронная сквозная инициализация всех модулей, начиная с главного модуля. На этом этапе вызывается метод init всех инициализируемых модулей, который в зависимости от модуля может быть асинхронным. Этот этап заканчивается только после вызова всех колбеков модулей.
  4. Колбек app.init. Здесь можно вызвать render главного модуля и записать получившийся результат например в document.body.
  5. На клиенте внутри колбека app.init необходимо вызвать app.bind() для выполнения клиентской инициализации и навешивания всех событий на DOM-элементы всех модулей.
  6. Приложение готово для работы.

Стоит отметить, что пункт 1, 2 и 4 могут иметь разную реализацию для клиентской и серверной частей приложения, а пункт 5 выполняется только на клиенте.