// Пример создания блока const btn = new UIButton({ name: "make", icon: "wow", value: "Сделать", }); #!+ // Рендер #!- btn.renderTo(document.body); #!+ // Меняем аттрибуты #!- btn.set({value: "Сделать хорошо!"});
<button type="{attrs.type}"> <!-- Важно: корневой класс добавляется автоматически --> #! <bem:mod name="with_icon" test="attrs.icon"/> #!+ <span bem:elem="icon" **fn:if="attrs.icon"** > #!+ <!-- Использование блока `icon` --> #!- <b:icon name="{attrs.icon}"/> #!- </span> #!+ <span bem:elem="value"> **{attrs.value}** <!-- аналог <fn:value>attrs.value </fn:value> --> #!- </span> </button>
.button { // Кнопка с иконкой &_with_icon { /*... */ } // Иконка &__icon { /*... */ } // Текст/Значение &__value { /*... */ } }
import feast from "feast"; #!+ // Связанне ресурсы import template from "feast-tpl!./button.html"; import styleSheet from "feast-css!./button.css"; #!- #!+ // Используемые блоки import UIIcon from "blocks/icon/icon"; #!- #!+ /** @class UIButton */ export default feast.Block.extend(/** @lends UIButton# */{ #! name: "button", // Название блока, оно же корневой css-класс #! template, // Шаблон #! styleSheet, // Стили #!+ blocks: { // Используемые блоки icon: UIIcon, // "название" => UI-Блок #!- }, #!+ defaults: { icon: null, type: "submit", value: "Submit", #!- } #!- });
app:render() ⬇ event ⬇ app:render()
<!-- Блок «Список писем» --> <div> <!-- Используем абстрактный UIDataset --> <b:dataset models="{attrs.models}" compact="{attrs.compact}" > <!-- Определяем итерируемую часть --> <fn:match name="dataset-item" args="model"> <b:letter-list-item key="{model.id}" model="{model}" /> </fn:match> </b:dataset> </div>
#!+ const datasetLetter = new UIDatasetLetters({ models: mailbox.getLetters(), }); #!- datasetLetter.renderTo(document.body); #!+ // Где-то в коде добавляются модели #!- letters.set([/* список моделей */]); #!+ // Ещё где-то меняется статус письма #!- letters[0].set("flags.unread", false);
// У элементов есть набор аттрибутов, // которые отвчеают за его внещний вид и размеры <b:button ico="delete" size="m" short borderless />
<b:layout use:mediator="layout-manager"> #!+ <fn:match name="left-column" args="layout"> #! <b:button short="{layout.leftColumn <= 4}"/> #!+ <fn:choose> <fn:when test="layout.is('xxs')"> <!-- ... --> </fn:when> #!+ <fn:when test="layout.isLessThen('xl') || layout.isLessThen('m')"> <!-- ... --> #!- </fn:when> #!- </fn:choose> </fn:match> <!-- И так далее --> #!- <fn:match name="main-frame" args="layout"/> </b:layout>
import feast from "feast"; export default feast.Mediator.create("layout-manager", { components: { #!+ "layout": { // название для обращения внутри медиатора #! "class": UILayout, #!+ "attrs": { // Список аттрибутов, которые будет изменять медиатор #!+ "size": (mediator) => mediator.size, "leftColumn": (mediator) => mediator.columnLeft, "rightColumn": (mediator) => mediator.columnRight, #!- "support3pane": (mediator) => mediator.support3pane #!- }, #!- }, #!+ "portal-menu": { "class": UIPortalMenu, "attrs": { /* ... */ }, #!+ // события, на которые подписывается медиатор #!- "events": ["layout:2pane", "layout:3pane"], #!- }, #!+ "headline": { "class": UIHeadline, "attrs": { /* ... */ }, #!- }, }, handleEvent(evt) { /* ... */ } });
import feast from "feast"; export default feast.Mediator.create("layout-manager", { components: { /* ... */ }, // Обработчик событий от компонентов по умолчанию // (можно указать свой для каждого компонента) handleEvent(evt) { const type = evt.type; switch (type) { case "layout:2pane": case "layout:3pane": // Обновляем свойство медиатора this.type = type.split(":")[1]; break; } // После этого все связанные блоки будут обновлены! } });
<!-- Feast Style --> <toolkit:btn text="Click me!" remit:click="tap" /> #!+ <!-- Toolkit Style --> <toolkit:btn><![CDATA[{ "text": "Click me!", "remit:click": "tap" #!- }]]></toolkit:btn>
// Создаем роутер на основе карты сайта (всех маршрутов) const app = Pilot.create(sitemap); #!+ // Функция для получения данные для приложения const getViewAttrs = () => ({ folder: app.model.folder, folders: app.model.folders, letters: app.model.letters, letter: app.model.letter, router: app, route: app.route, request: app.request, #!- }); #!+ // Создаём view-приложения #!- app.view = new UIApplication(getViewAttrs()); #!+ // Обновляем приложение на каждый routeend #!- app.on("routeend", () => app.view.set(getViewAttrs())); #!+ // Рендер приложения c удалением содержимого #!- app.view.renderTo(document.body, {replace: true});
export default { model: { // модели доступные всем маршрутам folders: () => Folder.find(), }, "#index": { url: { // описание маски и правил валидации pattern: "/:folder", }, model: { threads: (req) => Thread.find({folder: req.params.folder}) } }, "#other-page-id": { ... } };
В прошлом мирко-докладе «React, где скорость?», я уже проводил сравнение с React, цифры которого примерно такие-же, а то и медленней, чем у нашего даталиста в старой Почте.
Так что не буду приводить новых цифр, а просто попытаюсь объяснить, за счет чего мы выжимаем скорость.
#!+ // Тут же мы имеем такие штуки как <fn:attr name="..." value="..." test="..."/> #!- <fn:add-class name="..." test="..."/> #!+ // А главное <bem:mod name="..." test="..."/> <bem:mod name="..." value="..." test="..."/> #!- // и другие...