Повторное использование шаблонов

Лень - двигатель прогресса

Сегодня мы рассмотрим способ, позволяющий немного упростить себе жизнь при создании интерактивного сайта путем повторного использования шаблонов. Визуально результат будет примерно таким же, как при дублировании бизнес-логики в браузере, но ценой существенно меньших трудозатрат на разработку JavaScript-клиента, да и на серверной части тоже. Хотите узнать как это провернуть?

Небольшая ремарка, чтобы не было недопонимания из-за терминологии:

  • Шаблон(template) : HTML-документ с расширенным набором тегов, которые впоследствии используются для подстановки динамических данных.
  • Шаблонизатор(templating engine) : библиотека, позволяющая на основе шаблона (использующего определенный синтаксис дополнительных тегов) и динамических данных получить итоговый HTML-документ, пригодный для отображения в браузере.
  • Рендеринг(rendering) : в данном контексте — процесс, которым занимается шаблонизатор.

Общий принцип

Чтобы сразу в голове сложилась нужная картина, начнем с дополненной схемы из статьи про архитектуру интерактивных сайтов:

Повторное использование шаблонов

Если вкратце, то стандартный интерфейс внутренних сервисов, скрывающихся за блоком "Бизнес-логика", можно реализовать таким образом, чтобы он возвращал все необходимые данные для рендеринга шаблона плюс его имя. База шаблонов у всех общая, у каждого уникальное имя, каждый сервер интерфейсов (обоих) держит по копии всех шаблонов в памяти.

HTML интерфейс просто разбирает HTTP-запросы, отправляет на его основе сообщение(ия) внутренним сервисам, получает в ответ имя шаблона и данные для его заполнения, с помощью шаблонизатора рендерит итоговый HTML и отдает браузеру или роботу.

Интерфейс сериализованных данных(если он, как и обсуждалось ранее, работает через постоянное соединение с браузером) каждому подключившемуся клиенту первым делом отправляет JSON-объект с шаблонами, по крайней мере если их не особо много, иначе лучше "по запросу". При действии пользователя JavaScript-клиент отправляет сообщение с информацией, на его основе интерфейс сериализованных данных передает то же самое (а может и как-то модифицированное) сообщение внутреннему сервису, также получает в ответ имя шаблона и данные и перенаправляет их клиенту (возможно сконвертировав в другой формат). Клиенту остается передать их своему шаблонизатору и заменить результатом его работы какую-то часть уже имеющегося в окне браузера HTML-документа.

Рендеринг

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

  • Отсутствие внешних вызовов при рендеринге, то есть на входе только данные, если используются какие-то фильтры или что-то такое - они должны быть частью шаблонизатора.
  • Шаблонизатор должен иметь реализацию на JavaScript, так как будет исполняться в том числе и в браузере.

Да, многофункциональные шаблонизаторы вроде Jinja2 - это очень удобно, но конкретно в данном случае богатый ассортимент возможностей не уместен. Наиболее известный кроссплатформенный шаблонизатор, не обремененный ничем лишним, называется mustache. С его использованием иногда получаются довольно замысловатые конструкции, но зато он отлично подходит под этот сценарий использования и прост как три копейки, изучить можно за 5 минут, рекомендую.

В этой схеме напрашивается использование node.js для реализации HTML-интерфейса, что откроет доступ к многочисленным шаблонизаторам, реализованным исключительно на JavaScript. Тем более кроме рендеринга шаблонов эта часть проекта практически ничего и не делает. В качестве бонуса требование про отсутствие внешних вызовов станет не таким строгим, да и в целом, если минималистичное решение вроде mustache по каким-то идеологическим соображениям не устраивает - любой написанный для node.js шаблонизатор наверняка станет отличным выходом.

Структура шаблонов

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

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

  • Блока <head> документа
  • Видимой "шапки" сайта
  • Сайдбара(ов), если они не сильно зависят от основного контентом страниц
  • Видимого "подвала" сайта плюс тегов для подключения JavaScript

HTML-интерфейс при чтении их из файловой системы "склеивает" их в полные шаблоны для каждой страницы, просто конкатенацией или с использованием механизмов шаблонизатора. Интерфейс сериализованных данных "заворачивает" шаблоны страниц в JSON (или другой используемый формат) прямо в исходном виде для вставки в блок с основным контентом. Из "общесайтовых" шаблонов браузерному клиенту вероятно могут понадобиться только сайдбар(ы), и то не всегда.

Изменения в остальных частях сайта лучше все же отдать на совесть представлений на основе клиентского фреймворка. В первую очередь это касается изменения <title> и других мета-тегов.

Примечания

  • При использовании минималистичного шаблонизатора без внешних вызовов будьте морально готовы передавать ему "многоуровневые" объекты для вставки в шаблон. Например, если говорить о постраничной навигации, там, где в продвинутом шаблонизаторе было бы что-то вроде {% pagination(current_page, total_pages) %}, может понадобится не только написать саму верстку (что, в целом, хорошая практика), а еще и передать информацию о точном списке страниц, какая именно из них активная, где пропуски и пр.
  • Стоит обращать внимание на производительность используемого шаблонизатора. Например, под одну из платформ "официальная" реализация mustache, как оказалось, проигрывает сторонней с отрывом в 2 порядка.
  • Хоть при таком подходе добиться одинакового внешнего вида страниц при рендеринге серверной и клиентской частью достаточно легко, следить за их соответствием все же стоит - какие-то детали можно и упустить.

Заключение

Как я уже намекал в конце предыдущего материала, обсуждавшийся в этой статье подход не совсем "идеологически правильный", по крайней мере с точки зрения используемого клиентского фреймворка. Модели, вероятно, будут использоваться для хранения библиотеки шаблонов и данных для их рендеринга, а не для объектов предметной области проекта. Представления будут отвечать лишь за рендеринг шаблонов и синхронизацию второстепенных элементов интерфейса. Если Вы все же пойдете по этому пути, хочется, чтобы Вы сделали это осознанно. Альтернативный сценарий создания полноценного JavaScript-приложения для работы в браузере для некоторых проектов по-прежнему может оказаться более предпочтительным.

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

Эта статья - четвертая в серии про Интерактивные сайты, автор - Иван Блинков, основано на личном опыте. До встречи на страницах Insight IT!