Feast

Live coding & components

Fest vs. Feast

Fest vs. Feast

Feast Fest
Компиляция runtime server/proxy
Компоненты есть нет
Поддержка BEM есть нет
Расширяемость максимальная нет (только PR)
Синтаксис любой fat-xml
Рендер VDOM string
и так далее...

Компиляция

Компиляция

			// Прасим шаблон в подобие DOM дерева
			var template = feast.parse("<img src=\"{attrs.src}\"/>");
			#!+ // Создаем фнукцию генерации VDOM
			#!- var compiledTemplate = feast.compile(template);
			#!+ // Получаем фрагмент VDOM
			var fragment = compiledTemplate({src: "foo.png"});
			#!- // fragment: {tag: "img", attrs: {src: "foo.png"}}
			#!+
			// Присоединяем фрагмент-vdom к body
			#!- var virtualNode = feast.vdom.append(document.body, fragment);
		

Компиляция: Feast vs. Fest

Feast Fest
Настройка сервера (proxy/watch) не требуется обязательна
Размер итогового шаблона неизменен, либо меньше увеличивается
Работа в браузере да нет

Синтаксис

Синтаксис

Fest
					#!+ <a>
						#!+ <fest:attributes>
							#!+ <fest:if test="params.href">
								#!+ <fest:attribute name="href">
									#! <fest:value>params.href</fest:value>
								#!- </fest:attribute>
							#!- </fest:if>
						#!- </fest:attributes>
						#! <span>link</span>
					#!- </a>
				
Feast
					#!+ <a>
						#! <fn:attr
							#! name="href"
							#! value="{attrs.href}"
							#! test="attrs.href"/>
						#! <span>link</span>
					#!- </a>
				

Ещё один синтаксис

xhtml
					<!-- Комментарий к верстке -->
					<a bem:block="btn">
						<fn:attr
							name="href"
							value="#!{attrs.href}"/>

						<fn:if test="attrs.icon">
							<b:icon name="{attrs.icon}"/>
						</fn:if>

						<span bem:elem="text">link</span>
					</a>
				
xtpl
					#! // Комментарий к верстке
					#!+ a.btn {
						#! href: "#!" + attrs.href

						#!+ if attrs.icon {
							#! &icon { name: attrs.icon }
						#!- }

						#! span.&__text | link
					#!- }
				
			// Определяем блок
			&ctrl = a.ctrl | {{attrs.name}}

			table **>** tr {
				td {
					input[type="checkbox"] {
						id: "cbx"
						on-change: attrs.handleChecked(evt)
						checked: attrs.state
					}

					label[for="cbx"] | check me
				}

				td > &ctrl { name: "edit" } **+** &ctrl { name: "remove" }
			}
		

Свой синтаксис

Это большая отдельная тема, о который можно поговорить отдельно, если будет интерес.

Основная проблема не прасинге, а именно описательной возможности синтаксиса.

Расширяемость

Расширяемость

Как я уже говорил, fest можно расширить только через Pull Request, что согласитесь не очень удобно.

А feast?

feast: Модификаторы вывода

			<!-- Выведем время -->
			<fn:value>params.time</fn:value>
			#!+
			<!-- Форматируем -->
			#!- <fn:value>moment(params.time).fromNow()</fn:value>
			#!+
			<!-- Эээ, а как пробросить moment? -->
			<!-- Так? -->
			#!- <fn:value>require("momentjs")(params.time).fromNow()</fn:value>
		

feast: Модификаторы вывода

			import feast from "feast";
			import moment from "momentjs";
			#!+ // Определяем модификатор
			feast.mods["time:fromNow"] = function (value) {
				#! return moment(value).fromNow();
			#!- };
			#!+
			<!-- Используем -->
			#!- <fn:value **mod="time:fromNow"**>params.time</fn:value>
		

CustomTags

feast: CustomTags

			<my:loop from="1" to="{attrs.max}" as="val" test="attrs.enabled">
				<fn:value>val</fn:value>
			</my:loop>
		
			import feast from "feast";
			// Определяем тег с namespace (можно и без него)
			feast.tags["my:loop"] = {
				#! scope: true, // означает, что тег выводу не подлежит
				#! required: ["from", "to"], // обязательные атрибуты
				#! expressions: ["test"], // javascript выражения
				#!+ toCode() {
					#!+ return [
						#! "if ($test) for (var &as = $from; &as <= $to; &as++) {",
						#! "}"
					#!- ];
				#!- }
			};
		

feast: CustomTags

			// А что, если я хочу использовать namespace?
			<use:foo/>
			<use:bar/>
			<use:baz/>
		

feast: Custom namespace

			import feast from "feast";
			// Совпадение на любой тег в определенном namespace
			feast.tags["use:*"] = {
				scope: true,
				toCode(attrs, node) {
					const [ns, name] = node.name.split(":");
					// ...
				}
			};
		

feast: CustomTags

Приоритеты поиска совпадений

			feast.tags["ns:tag-name"] = {}; // максимальный приоритет
			feast.tags["ns:*"] = {}; // средний
			feast.tags["*:tag-name"] = {}; // минимальный
		

Компоненты

feast

В первую очередь, feast — это инструмент, который помогает быстро и гибко разрабатывать и поддерживать блоки проекта, а не очередной шаблонизатор и тем более framework.

Запуск GUI для работы

			cd ./feast/
			npm start

			> feast@0.10.0 start /Users/RubaXa/Dropbox/git/feast
			> node feast --playground=./blocks/

			Feast running at http://127.0.0.1:2015/
		

Создание блока (по умолчанию)

			./blocks/
				./checkbox/
						checkbox.html     // шаблон
						checkbox.js       // js-реализация
						checkbox.scss     // стили
						checkbox.spec.js  // спецификация
		

checkbox.html и checkbox.scss

			<div>
				<!-- содержимое блока (если честно, этого комментария нет) -->
			</div>
		
			.checkbox {
				padding: 10px;
				background: red;
			}
		

Пока всё

Сейчас я покажу, как происходит верстка и тестирование верстки.

checkbox.spec.js

			export default {
				cases: {
					"base": {attrs: {}},
					"checked": {
						attrs: {
							checked: true,
							$tests: [ // тесты
							{
								msg: "выделен",
								mods: ["checked"]
							},
							{trigger: "click"},
							{
								msg: "невыделен",
								mods: []
							}]
						}
					}
				}
			};
		

checkbox.html

			<div on-click="_this.set('checked', !attrs.checked)">
				<bem:mod name="checked" test="attrs.checked"/>
				<div bem:elem="checkmark"></div>
			</div>
		

on-click — отстой, фу так писать!

remit:{event}

remit:{event}

			<div **remit:click="toggle"**>
				<bem:mod name="checked" test="attrs.checked"/>
				<div bem:elem="checkmark"></div>
			</div>
		

remit:{event}

			import feast from "feast";
			import template from "feast-tpl!./checkbox.html";
			/** @class UICheckbox */
			export default feast.Block.extend(/** UICheckbox# */{
				#! name: "checkbox",    // название блока
				#! template: template,  // шаблон
				#!+ events: {            // обработка событий (не DOM)
					#! "toggle": "handleToggle"
				#!- },
				#!+ handleToggle() { 
					this.set("checked", !this.attrs.checked);
				#!- }
			});
		

Использование

Использование

			<!-- Было: -->
			<fest:get name="checkbox">{checked: true}</fest:get>
			<fest:get name="checkbox">{checked: attrs.state}</fest:get>

			<!-- Стало: -->
			<b:checkbox checked/>
			<b:checkbox checked="{attrs.state}"/>
		

Screencast

The End