Разметка. Часть 2: Шаблонизаторы.
И так, в прошлый раз я закончил на AsyncTpl. Это XML/Smarty-based шаблонизатор, кроме этого, по сравнению с подобными решениями он умел:
- Выводить ошибки
- Работать на сервере и клиенте
- Имел зачатки data-binding
- Основан на самописном XML-парсере (очень простом)
- Был очень быстрым ;]
А дальше я начал всё больше замечать, что XML слишком строг, а главное многословен и чем больше логики в него засовываешь, тем страшнее становиться.
Поэтому я начал смотреть в сторону альтернативного синтаксиса…
Так появился xtpl :]
Да, он похож на Jade/Pug, но давайте по порядку, как же я до этого докатился.
Дело в том, что мне нужен был синтаксис, который можно понять без подготовки и он не должен быть XML-подобный. На ум приходит только одно, любой web-разработчик знаком с CSS и XPath, так почему бы не сделать на его основе синтаксис.
И вот с этой мысли всё и начинается. Достаточно быстро находиться HAML, Jade/Pug и вот вроде бы ответ, но проблема в том, что Pug атрибуты объявляются через input(...)
-нотацию, а не [...]
, а ещё он tab-based, что мне до сих пор кажется спорным, но поддержку добавил и постепенно пробую, как с этим жить.
Ремарка: Про Slim узнал намного позже, он подходит на 80%
Вот с этого момента и начались мои эксперименты, которые продолжаются и по сей день.
И так, как я уже написал выше, синтаксис должен быть понятным, поэтому я его сделал похожим на CSS.
Всё достаточно просто
tagName#id.className.otherClassName[attr="value"][attrBoolean] {
propName: propValue; // значение всегда JS выражение
on-eventName: ctx.handleClick(); // Вызов функции // Вложенный тег, если имя не заданно, то по умолчанию div
.headerClassName | Wow, text! // Вложенный ui-блок (компонент)
&blockName {
x-attrName: "...";
// Вложенный контент
input.text[type="text"] { }
}
}
UI-Блоки определялись прямо в шаблоне
&blockName = label {
class.someName: ctx.expression; // Место для вставки внешнего контента (slot)
x-content {}
}
Ещё была поддержка директив
form.b-form[action="{ctx.action}"] {
x-derectiveName: {...ctx.options}
// ...
}
Собственно всё. Основной момент был в том, что xtpl продолжал работать как строковой шаблонизатор (подробнее), но после первичного рендера сканировал полученную DOM-структуру и связывал ноды с данными, на основе которых они построить, чтобы получить точечный биндинг.
Да, звучит как изоморфный рендер (когда ещё это не было мейнстримом ;]), но я тогда об этом не думал и в какой-то момент это стало слишком сложно и я решил отбросить серверный рендер.
IDE
Свой синтаксис это круто, только вот без поддержки IDE, это всё нахрен не нужно, как и шаблонизатор без вывода ошибок. Поэтому я упоролся окончательно и написал plugin для подсветки и автокомплита кода в WebStorm.
Кроме стандартных решений в стиле Pug/Jade, есть и отличия, например xtpl поддерживал ещё и
.foo > b.bar | …
⟶<div class="foo><b class="bar">...</b></div>
.ico + .ico
⟶<div class="icon"></div><div class="icon"></div>
b.cbx > b.&__mark
⟶<b class="cbx"><b class="cbx__mark"></b></b>
Такие конструкции не только позволяют писать максимально компактный код, но и в купе с подсветкой IDE дают максимально низкий порог входа в подобный новодел. Достаточно сказать разработчику, что это CSS-like синтаксис и вопросов будет минимально.
Всё это не голословные утверждения, в отличии от AsyncTpl, xtpl нашел своё применение.
Таким проектом стал Рестордер, который увы и ах, не «взлетел».
Это была b2b площадка для Поставщиков и Ресторанов, но главное, это был Single Page Application с xtpl на борту и удаленным верстальщиком в команде (я отвечал за архитектуру в целом и js конечно). И как показал этот опыт, человек без опыта работы с подобными шаблонизаторами смог спокойно верстать без лишних вопросов.
Самое удивительное в этом всём, что проблемы конечно были, но минимальные, вывод из этого один, пишите тесты, больше и чаще. Никогда не гнушайтесь проверить граничные и заведомо ошибочные или невозможные сценарии, рано или поздно, они произойдут ;]
На этом всё, ещё раз оставлю ссылку на слайды с xtpl и уже перейду к Exility :]