Войти с помощью github

Кто каким пользуется? И у какого больше переспективы на существование, поддержку, развитие )))

давайте свои за и против))))

Привет. У нас есть несколько вещей, которые мы хотим сначала объявить deprecated, а затем оторвать в одной из следующих версий. Если вы не встречались ни с одной из этих вещей, я не буду удивлен, и посчитаю это подтверждением правильности наших действий.

once()

Выпиливаем по двум причинам.

  1. once не работает так как ожидается. По названию можно подумать, что шаблон применится один раз в рамках узла. Но по факту он применится один раз в рамках всего прохода apply().
  2. Очевидно once никому не нужен, так как этот баг даже не замечали до недавнего времени.

Есть важный момент про сохранение обратной совместимости с bem-xjst 1.x и мы про него помним. Поэтому once будет объявлен deprecated, но оторван только тогда, когда пользователей bem-xjst 1.x совсем не останется.

this.isArray()

Рекомендуем использовать Array.isArray.

this.isSimple()

  1. Есть ощущение, что метод никому не нужен.
  2. Метод слегка отстал от текущей спецификации:
this.isSimple(Symbol('foo')); // Вернет false, хотя символ это примитив.

local()

Метод не нужен и мы не видим для него никакого практического применения. Мы решили объявить его deprecated, чтобы не путать вас и не мучать догадками зачем же оно нужно.

Итого

Есть по перечисленным методам есть замечания или возражения — пишите.

https://github.com/tadatuta/bem-express/blob/master/common.blocks/root/root.bemtree.js В bemtree шаблоне явно указывается index.css и index.js Если бандлов будет несколько как поставить правильные ссылки на статику? Или, например, в зависимости от типа страницы - нам нужно указать модификатор и тему блоку page?

Планирую написать сайт на БЭМ и django. Взаимодействие будет реализовано как написал Владимир тут https://ru.bem.info/forum/483/#comment-120414533

  1. Наружу торчит какой-нибудь балансер (например, nginx).
  2. За ним стоит node.js сервер, который ходит в джангу за данными (rowData). Это может быть как один запрос так и агрегация нескольких запросов по http или через unix-socket. Помимо Django нода может собирать данные из любых других необходимых источников (сторонних API, etc). Соответственно Django ничего не знает про view-часть и служит универсальным источником данных для десктопа, телефонной веб-версии или мобильного приложения.
  3. Полученный от Django ответ rowData отдается первому слою преобразования (например, с помощью BEMTREE и на выходе получается BEMJSON — т.е. это по-прежнему данные, но уже знающие про view.
  4. Далее BEMJSON передается в BEMHTML/BH и на выходе получается HTML.

Но я не хочу переносить какую-либо бизнес логику в nodejs. Nodejs будет лишь принимать запросы и проксировать их в django. Django будет возвращать данные только в JSON. При этом помимо самих данных бэкенд будет предоставлять дополнительные данные для БЭМ стека. Например, имя бандла, который должен принять эти данные и сформировать HTML.

Таким образом, можно будет воспользоваться всеми преимущесвами построения интерфейса на БЭМ, но при этом оставить всю бизнес-логику в django, не дублировать логику (DRY)

На стороне django будут реализованы все необходимые урлы, вьюхи. Вьюхи полагаю будут в основном на django-rest-framework + доп. данные для БЭМ. А вот на стороне nodejs будет простейший proxy сервер на express. Proxy сервер будет принимать запросы от браузера, проксировать их в django и получать в ответ JSON-данные. В этих данных будут дополнительные поля для БЭМ (возможно лучше передавать эти данные в заголовках ответа), например, имя бандла. А далее эти данные передаем в BEMTREE шаблон нужного бандла, получаем bemjson, далее этот bemjson передаем в BEMHTML шаблон и на выходе получаем html-код страницы, которуый передаем в браузер.

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

Еще хотелось бы помощи в реализации proxy-сервера на nodejs. Пробовал использовать https://www.npmjs.com/package/http-proxy но так и не понял как его доработать, чтоб возвращать в браузер не json-ответ от django, а подменять его сформированным в БЭМ html. Может у кого есть примеры подобных реализаций? Поделитесь опытом.

Уже как то заходила речь о там что было бы круто выполнять bemtree в php. Это бы позволило сильно упростить написание шаблонов и код почище бы стал.

Можете что-то посоветовать, как организовать шаблонизацию не имея bemtree и не усложняя шаблоны блоков?

Парни поделитесь удачным на ваш взгляд клиент-сервер решением обработки ошибок форм? Ajax+js или все на сервере удобнее? @voischev https://twitter.com/voischev/status/703501615681179650

Идеально Model -> JSON Schema -> BEMTREE -> BEMJSON в качестве JSON Schema хорошо подойдет OpenAPI-Specification. Потрогать ее можно тут editor.swagger.io (без БЭМ терминов).

Важное из того что я хотел сказать, что первичная реализация валидации должна быть на серверной стороне, используя спецификацию (хорошо подходит OpenAPI Specification лучшего решения не знаю, может есть еще?) мы можем сообщить браузеру о необходимых проверках. Такой подход позволит всегда держать фронтенд в курсе необходимых проверок модели. Единственная зависимость это клиентская реализация валидаторов которые будут описаны в JSON Schema модели которую сообщит сервер.

Это мое личное мнение, хочется узнать опыт сообщества.

В основе каждой страницы сайта лежит блок page. Как можно дополнить этот блок, чтоб добавить для него дополнительные теги?

Интересует возможность добавить ряд статичных meta-тегов в head, прописать favicon (может быть еще что то). Идея в том, чтобы задать все это в одном месте, а не прописывать в bemjson-е каждого бандла. Подобные вещи реализуются в BEMHTML-шаблонах. Но блок page уже имеет свой шаблон и нужно как то его дополнить, но при этом не сломать. Хотелось бы пример подобной реализации.

Спасибо!

Всем привет. Не нашел такого компонента, если у кого нибудь есть, поделитесь пожалуйста. Нужно сделать в точности такое: http://getbootstrap.com/javascript/#collapse-example-accordion

var fs = require('fs'), path = require('path'), vm = require('vm'), vow = require('vow');

    var Bundle = function(name, folder){
        this.name = name+'.bundles';
        this.folder = folder;   
        this.path = path.join(this.name,this.folder,this.folder);
        this.BEMTREE = this.bemtree();
        this.BEMHTML = require('../../../' + this.path + '.bemhtml.js');
    };

    Bundle.prototype.bemtree = function(){
         var bemtreePath = './' + this.path + '.bemtree.js',
         bemtreeFile = fs.readFileSync(bemtreePath, 'utf-8'),
         context = vm.createContext({
            console: console,
                Vow: vow,
                require: require,
            setImmediate: setImmediate          
        });

        vm.runInContext(bemtreeFile, context);
        return context.BEMTREE;
    };
    module.exports = Bundle;

    var Bundle = require('../server/libs/bem');
    var index = new Bundle('desktop', 'index');

    index.BEMTREE.apply({ block: 'header' }).then(function(bemjson){
        console.log(bemjson);
    });

evalmachine.:1823 throw new Error(msg || 'Assertion failed'); ^

Error: Wrong .match() argument at assert (evalmachine.:1823:11) at Tree.match (evalmachine.:1613:5) at Object. (evalmachine.:1967:1) at Tree.build (evalmachine.:1517:13) at BEMHTML.compile (evalmachine.:372:18) at buildBemXjst (evalmachine.:1844:5) at evalmachine.:2080:15 at evalmachine.:2083:3 at Object.exports.runInContext (vm.js:44:17) at Bundle.bemtree (/home/rustam/www/rhblog/server/libs/bem/index.js:24:5)

Пример из "Верстка для самых маленьких. Верстаем страницу по БЭМу"

block('menu').elem('item').elemMod('active', true)

Почему не срабатывает elemMod?

ссылка на песочницу

Не получается использовать mix, не указав block или elem:

block('header')(
  mix()([
    { block: 'row', mods: { 'sac': true }},
    { block: 'section' }
  ]),
  content()({
    mix: {
      block: 'row',
      elem: 'col',
      mods: { s: true }
    },
    content: [
      { elem: 'title' },
      { elem: 'intro' }
    ]
  })
)
<div class="header row row_sac section">
    <div>
        <div class="header__title"></div>
        <div class="header__intro"></div>
    </div>
</div>

Песочница С элементом Если указать миксуемый элемент прямой на этой ноде, то придётся явно указывать block у элементов в контенте.

Можно ли заставить работать первый вариант?

Проверил возможность использовать БЭМ-стек в песочницах. Учитывая наличие dist-поставки bem-core-dist и bem-components-dist препятствий для этого быть не должно.

Мотивация

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

Эксперимент

Для эксперимента взял блок hello из статьи Собираем статическую страницу на БЭМ. Вот что получилось:

Шаблоны заворачиваем в модуль:

bemhtml

modules.define('BEMHTML', [], function (provide, BEMHTML) {
  BEMHTML.compile(function() {
    block('hello')(
      js()(true),
      tag()('form')
    );
  });
  provide(BEMHTML);
} );

bh

modules.define('BH', [], function (provide, bh) {
  bh.match('hello', function (ctx) {
    ctx.js(true);
    ctx.tag('form');
  } );
  provide(bh);
} );

Для возможности использовать bemjson реализовал блок:

/**
 * @module bemjson
 */
modules.define(
  'bemjson', ['i-bem__dom', 'BEMHTML'],
  function(provide, BEMDOM, BEMHTML) {

    /**
     * @exports
     * @class bemjson
     * @bem
     */
    provide(BEMDOM.decl(this.name, /** @lends bemjson.prototype */ {
      onSetMod: {
        js: {
          inited: function() {
            BEMDOM.replace(this.domElem, BEMHTML.apply(JSON.parse(this.domElem.text())));
          }
        }
      }
    }));
  }
);

Пример использования:

<script type="text/bemjson" class="bemjson i-bem" data-bem="{&quot;bemjson&quot;: {}}">
{
 "block": "hello",
 "content": ""
}
</script>

Результаты

В проверенных песочницах все отлично работает. Plunker и CodePen дают больше свободы и поэтому больше подходят для экспериментов с БЭМ-стеком, а Pluker больше всего, потому что позволяет все разложить по отдельным файлам.

К сожалению в IE8 не работает хотя все необходимые ресурсы (shim, ie.css) для этого подключены в Plunker и CodePen кроме JSFiddle, ошибка:

'modules' is undefined

Вопросы

  • Какие есть варианты оформить bemjson отдельным файлом в Plunker?
  • Может быть кто-то знает более подходящею песочницу для экспериментов с БЭМ-стеком?
  • Тестирование, приветствуется.

UPD: v2

  • Каким образом можно оформить bemhtml в песочнице?

На выходе после BEMTREE шаблонизации выходит примерно такой bemjson

{
    block: 'video'
    content: [
        {
            elem: 'media',
            urls: data.links,
        },
        {
            elem: 'content',
            content: [
                {
                    elem: 'title',
                    content: data.title
                },
                {
                    elem: 'description',
                    content: data.description
                }

            ]
        }
    ]
}

Элемент __media с помощью bemhtml потом оборачивается в блок wrapper-block Что нужно написать в блоке wrapper-block, чтобы он не менял в контексте block, чтобы не делать вот так:

{
    block: 'video'
    content: [
        {
            block: this.block,
            elem: 'media',
            urls: data.links,
        },
        {
            block: this.block,
            elem: 'content',
            content: [
                {
                    block: this.block,
                    elem: 'title',
                    content: data.title
                },
                {
                    block: this.block,
                    elem: 'description',
                    content: data.description
                }

            ]
        }
    ]
}

Допустим есть функция someFunc которая принимает строку, и возврщает модифицированную строку, как мне ее внеднрить в свои шаблоны? Если можно отдельным блоком, то как лучше это сделать? Или же если дбавлять метод к контексту (this.someFunc) то где лучше его объявить?

Введение

На прошлых вебинарах с Димой Белицким мы сверстали простую страницу следуя БЭМ-методологии: написали HTML и CSS, немного декларативного JavaScript, а также настроили сборку с помощью gulp.

В этом вебинаре мы получим BEMJSON имеющихся страниц, напишем простые BEMHTML-шаблоны и посмотрим на некоторые интересные возможности BEMHTML.

Итак, немного про декларативные шаблоны.

Все привыкли к декларативной природе CSS, поэтому удобно провести аналогию.

Допустим, у нас есть список:

<ul class="menu">
  <li class="menu__item">
    Привет
  <li>
  <li class="menu__item">
    BEM!
  <li>
</ul>

Чтобы задать определенный внешний вид всем элементам списка можно написать CSS:

.menu__item {
    background: red;
}

А если же хочется изменить связку ul + li, например, на nav + a? Что если это можно было бы сделать по аналогии с CSS, написав соответствующий «селектор» и просто задать другое значение свойству tag?

Так это выглядит в BEMHTML:

block('menu')(
  tag()('nav')
)

block('menu').elem('item')(
  tag()('a')
)

Чтобы иметь возможность таким образом пребразовывать HTML необходима особая декларация страницы.

BEMJSON

BEMJSON — это описание структуры страницы в терминах БЭМ на JavaScript с зарезервированными полями. Указанный ранее список в BEMJSON описывается так:

({
  block: 'menu',
  tag: 'ul',
  content: [
    {
      elem: 'item',
      tag: 'li',
      content: 'Привет'
    },
    {
      elem: 'item',
      tag: 'li',
      content: 'BEM!'
    }
  ]
})

BEMJSON компилирутся в HTML.

Избавиться от копипасты tag: 'li' поможет BEMHTML-шаблон.

block('menu')(
  tag()('ul')
)

block('menu').elem('item')(
  tag()('li')
)

BEMHTML-шаблон накладывается на BEMJSON по аналогии с тем, как CSS накладывается на DOM-дерево. На выходе — получаем HTML.

BEMJSON для наших страниц

Код — 01858d

Если есть HTML, написанный по БЭМ, то BEMJSON можно получить автоматически с помощью пакета html2bemjson

npm install html2bemjson

Сборка

Код — eef725

Далее нужно дотюнить сборку:

  • gulp научился собирать зависимости по BEMJSON, а не HTML
  • добавился таск для компиляции HTML по BEMJSON с использованием BEMHTML-шаблонов

    Вычисления во время компиляции в BEMJSON

Код — 51ec21

BEMJSON представляет собой plain JavaScript, поэтому позволяет любые вычисления во время компиляции — то, чего не достает HTML.

Например,

({
  block: 'menu',
  // построим BEMJSON списка динамически из массива текстов элементов
  content: [
    'Привет',
    'BEM!'
  ].map(function(text) {
    return {
      elem: 'item',
      content: text
    };
  })
})
({
  block: 'menu',
  // определим массив текстов динамически,
  // который будет разным при каждой генерации итогового HTML
  content: (function() {
    return Math.random() > 0.5 ? ['Привет'] : ['Привет', 'BEM!'];
  }()).map(function(text) {
    return {
      elem: 'item',
      content: text
    };
  })
})

Напишем простые BEMHTML-шаблоны

Код — 17d9a6

Упростим полученный автоматической конвертацией BEMJSON:

  • вынесем всю HTML-обвязку страницы в блок page
  • упростим представление меню в BEMJSON, сгенерировав BEMJSON самого списка элементов в шаблоне
  • напишем простой шаблон для ссылки

    Специальные возможности BEMHTML

Код — bad312

Шаблонизатор BEMHTML имеет некоторые интересные возможности:

Переопределение в зависимости от входных данных

Например, блок link должен быть представлен в HTML span-ом, а не ссылкой, если в BEMJSON не было поля url.

Переопределение на уровне переопределения

Например, менюшка на странице с Поттером должна быть в HTML представлена тегом ol, а на всех других страницах — ul. Для этого достаточно определить блок меню на уровне potter.blocks. После сборки всех шаблонов в один файл получим:

// common.blocks
block('menu')(...)

// potter.blocks
block('menu')(...)

По аналогии с CSS, последнее «правило» перебивает предыдущее.

Допустим, я хочу чтобы шаблоны компилились по технологии require('enb-bemxjst/techs/bemhtml'), а bemjson в html по технологии require('enb-bh/techs/bemjson-to-html'). То есть шаблоны я прогоняю через BEMHTML, а bemjson через BH. Как это сделать, чтобы не было ошибок при сборке?

{
    elem: 'link',
    mix: { block: 'link' }
}

Знаю только один способ, который в моем случае не подходит, это просто поменять местами

{
    block: 'link',
    mix: { block: this.block, elem: 'link'}
}

Задача такая: есть макеты на запросы (site/index, video/index, video/new, video/edit и тд) Как грамотно оргинизовать их шаблоны. Используется весь стэк (BEMHTML, BEMTREE)

Если на каждого заводить по бандлу, то как тогда орагнизовать root.bemtree? site/index video/index video/new

Или же лучше заводить по модификатору на page page_view_site-index page_view_video-index page_view_video-new В этом случае тогда будет один бандл и один большой css и js файл

Какой здесь best practice?

Сборка на основе bemdecl. Вот grep:

% grep -r root ./desktop.bundles/*        
./desktop.bundles/index/index.bemtree.js:        if ($$block === "root" && !$$elem && (__$ctx.__$a0 & 1) === 0) {
./desktop.bundles/index/index.bemtree.js:    console.log("root");
./desktop.bundles/index/index.deps.js:        "block": "root"
./desktop.bundles/index/index.bemdecl.js:    { name: 'root' },

Сам ./desktop.blocks/root/root.bemtree.js

block('root').replace()(function() {
    var data = this.data = this.ctx.datal;
    console.log('root');

    return {
        block: 'page',
        favicon: '/favicon.ico',
        title: data.title,
        mods: { view: data.view },
        styles: [
            { elem: 'css', url: '/index.min.css' }
        ],
        scripts: [
            { elem: 'js', url: '/index.min.js' }
        ]
    };
});

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

Делаю свой первый проект с использованием bemtree. При разработке блоков в контексте страницы возникает сложность в отношении того, куда правильно прописывать deps. Например есть файл основного блока, допустим он называется content с элементом column в него включается блок sidebar c элементом item и блок title c элементом level Можно прописать все deps в файле основного блока content, а можно раскидать зависимые deps элементов по вложенным блокам. Как правильно прописать deps в данном случае? Есть ли какая-то последовательность записи блоков и элементов или главное, чтобы они были вызваны?

({
    shouldDeps: [
       { elems: ['column'] }, { elems: ['side'] }, { elems: ['border'] }, 'sidebar', 'title'
    ]
})

Добре люди!

Занимаюсь формированием тестов через enb-bem-specs коя использует bem-pr

Так вот, при формировании странички блок spec в bem-pr перетирает себя так

block spec, default: applyCtx({ block: 'mocha' })

Но стало понятно, что для тестирование некоторых блоков нам нужно куда-то проинициализировать заранее шаблоны. Сделать это легко можно посредством BH (BEMHTML). Чтобы не нарушать идилию с блоком mocha появилась идея добавить еще один блок spec-content

Было (bem-pr/spec.blocks/spec/spec.bh.js):

module.exports = function(bh) {

bh.match('spec', function() {
    return { block : 'mocha', attrs: { id: 'mocha'} };
});

};

Стало:

module.exports = function(bh) {

bh.match('spec', function(ctx) {
    ctx.tag(null);
    ctx.content([
        {
            block: 'mocha'
        },
        {
            block: 'spec-content'
        }
    ]);
});

};

Подскажите, как будет верно тоже самое реализовать в BEMHTML, для того же блока?

Есть код в .bemhtml

block('button').mod('theme', 'theme001').content()(
    function(){
        return [this.ctx.icon,' ',this.ctx.text]
});

Вопрос: Как блок "button" обернуть в родительский блок "link" средствами .bemhtml?

Спасибо!

В документации написано, что

  • синтаксис BEMHTML и BEMTREE почти идентичен.
  • BEMTREE использует синтаксис-BEM-XJST и сохраняет все особенности BEM-XJST-шаблонизаторов.

На практике использую синтаксис BEMJSON из других проектов в BEMTREE и все работает. Как правильно писать bemree синтаксис? И в чем отличие от BEMJSON? Понимаю, что вопрос достаточно обширный поэтому прошу указать на важные различия.

Решил поменять имя основного класса c page на root в блоке root после этого перестало передаваться все содержимое в head.

block('root').replace()(function() {
    return {
        block: 'main', //здесь изменено имя
        title: 'TODO',
        head: [
            { elem: 'css', url: 'index.min.css' },
            { elem: 'css', url: '//fonts.googleapis.com/css?family=Open+Sans:300&subset=cyrillic'}
        ],
        scripts: [
            { elem: 'js', url: 'index.min.js' }
        ],
        mods: { theme: 'islands' }
    };
});
  1. Допустим есть 10-20 страниц верстки. Шапка и подвал на этих 20 страницах повторяются 1 в 1. Хочешь что-то изменить? - 20 copy&paste в bemson. Это мой текущий уровень понимания. А нет ли случаем возможности выносить повторяющиеся части страниц в отдельные bemjson-файлы, чтобы править в одном месте? Я попробовал require в bemjson - работает, но не до конца. При повторной сборке bemjson-to-bemdecl сборщик (не зная про инклуды) считает что bemjson не изменился и естественно не перегенерирует bemdecl. Кто как решает эту DRY-задачку?
  2. Нет ли какой-нибудь технологии, которая на основе bemjson+bh сформирует список зависимостей как это сделано при сборке bemjson->bemdecl->deps? Очень надоедает для каждой bh-реализации руками писать deps.js

В библиотеке bem-components есть блоки menu и menu-item, тогда как в bem.info используется блок nav. Хотел бы узнать почему в bem.info не используются блоки меню из bem-components? А также как сгенерировать блок меню с помощью технологии bemtree? В примерах есть шаблоны для bemjson и html. Также есть код deps. Почему нет кода для bemtree?

Имеем следующую структуру BEMJSON:

...
{
    block: 'info',
    content: [
        {
            block: 'text',
            mix: { block: 'info', elem: 'title' },
            content: 'Some text'
        },
        ...
    ]
}
...

info__title.bemhtml :

block('info').elem('title')(
    tag()('h1')
)

На выходе получаем (стили из info__title.styl успешно парсятся — зависимости соблюдены) :

<div class="text info__title">Some text</div>

В чем ошибка?

У нас есть документация про технологии БЭМ платформы (BEMJSON, BEMTREE, BEMHTML, DEPS), но практически нет туториалов по использованию их всех вместе. Есть несколько исключений, но они достаточно объемные и сложноваты для восприятия.

Я написал простой пример, рассчитанный на тех, кто уже немного освоился с версткой на основе project-stub и хочет продолжить свое знакомство с платформой.

Пусть у нас есть следующий файл с данными data.json:

{
    "user": "mathetes",
    "company": "WebExcel",
    "city": "Novosibirsk"
};

Как вариант, данные могут приходить из БД или через HTTP API — источник не играет роли.

Наша задача сгенерировать на основе этих данных HTML, который будет представлять собой страницу с логотипом в шапке, карточкой пользователя в основной части и копирайтом в подвале.

Первым шагом необходимо из исходных сырых данных получить BEMJSON, описывающий страницу. Для этого будем использовать технологию BEMTREE. При этом договоримся, что в качестве корневого блока, на основе которого будет строиться дерево, возьмем блок page.

В результате должен получиться следующий BEMJSON:

{
    block: 'page',
    content: [
        {
            block: 'header',
            content: {
                block: 'logo'
            }
        },
        {
            block: 'main',
            content: {
                block: 'user',
                content: 'тут-содержимое-карточки-пользователя'
            }
        },
        {
            block: 'footer',
            content: '© 2015 SuperPuper'
        }
    ]
}

BEMTREE-шаблон для блока page должен построить шапку, основную часть и подвал:

block('page').content()(function() {
    return [
        { block: 'header' },
        { block: 'main' },
        { block: 'footer' }
    ];
]);

По техзаданию в шапке должен быть логотип. Тогда шаблон шапки может выглядеть так:

block('header').content()(function(){
    return { block: 'logo' };
});

В основной части нужна карточка пользователя. Так что нам потребуется доступ к данным из файла data.json. Но пока отложим этот момент и захардкодим какие-то тестовые данные:

block('main').content()(function() {
    return {
        block: 'user',
        content: [
            {
                elem: 'name',
                content: 'test name'
            },
            {
                elem: 'company',
                content: 'test company'
            },
            {
                elem: 'city',
                content: 'test city'
            }
        ]
    };
});

В подвале нужен копирайт:

block('footer').content()('© 2015 SuperPuper');

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

В самом простом случае мы можем сохранить все шаблоны в один файл, установить пакет bem-xjst и с его помощью скомпилировать бандл:

bem-xjst -i path/to/templates.js -o bundle.bemtree.js

Но раз мы хотим следовать рекомендации БЭМ методологии и раскладывать каждый шаблон в папку соответствующего блока, то нам потребуется какой-то способ потом эти шаблоны собрать вместе. Для этого подойдет сборщик ENB.

Схема работы ENB подробно описана в этом документе. Главное, что нас сейчас интересует — это то, что ENB собирает файлы только тех сущностей, которые явно задекларированы.

Получить декларацию с перечислением всех нужных блоков можно двумя способами: в *.bemdecl.js перечислить все нужные блоки (и не забывать добавлять и удалять их по мере разработки и рефакторинга), либо указать только корневой блок (в нашем случае page), а блоки, которые нужны корневому и всем последующим, указывать в их собственных списках зависимостей — deps.js. Второй путь гораздо гибче: сохраняется прицип БЭМ-методологии о том, что блок сам знает о себе всё, при удалении блока автоматически будут удалены и его зависимости, а при добавлении они автоматически включатся в сборку.

Так как шаблон блока page создает блоки header, main и footer, мы явно укажем это в списке зависимостей в файле page.deps.js:

({
    shouldDeps: ['header', 'main', 'footer']
})

Если вы имели опыт работы с project-stub, где нужные файлы попадали в сборку автоматически, то необходимость указывать зависимости вручную может показаться странной. Дело в том, что там у нас на руках заранее был готовый BEMJSON-файл, по которому можно было получить список всех необходимых сущностей. А в данном случае мы планируем генерировать BEMJSON в процессе сборки на основе шаблонов. При этом шаблоны необходимо собрать заранее, а значит декларацию нужных блоков потребуется описать самостоятельно.

Отлично, теперь мы знаем как собрать шаблоны. Следующим шагом необходимо получить с их помощью BEMJSON на основе данных, а затем из BEMJSON сгенерировать HTML с помощью BEMHTML. В общем виде это выглядит так:

var data = require('path/to/data.json'),
    BEMTREE = require('path/to/bundle.bemtree.js').BEMTREE,
    BEMHTML = require('path/to/bundle.bemhtml.js').BEMHTML,
    bemjson = BEMTREE.apply(data),
    html = BEMHTML.apply(bemjson);

require('fs').writeFileSync('index.html', html);

Эти преобразования будут работать и в браузере, если подключить bundle.bemtree.js и bundle.bemhtml.js на страницу. Останется только вставить полученную HTML-строку в DOM.

Осталось разобраться, как все-таки сгенерировать карточку пользователя на основе данных из data.json, вместо использования хардкода.

Как видно в примере кода выше, данные мы передаем в вызов BEMTREE.apply(data). При этом мы помним, что корневым блоком должен оказаться блок page. Достичь этого можно следующим образом:

var data = require('path/to/data.json');
BEMTREE.apply({
    block: 'page',
    data: data // теперь данные попадут в контекст шаблона блока page
});

Модифицируем код шаблона так, чтобы пробросить данные для вложенных в page блоков:

block('page').content()(function() {
    this.data = this.ctx.data; // this будет общим для всех потомков page,
                               // так что они смогут использовать поле data

    return [
        { block: 'header' },
        { block: 'main' },
        { block: 'footer' }
    ];
]);

Тогда финальный вид BEMTREE-шаблона блока main окажется таким:

block('main').content()(function() {
    var data = this.data;

    return {
        block: 'user',
        content: [
            {
                elem: 'name',
                content: data.user
            },
            {
                elem: 'company',
                content: data.company
            },
            {
                elem: 'city',
                content: data.city
            }
        ]
    };
});

Из соображений унификации в качестве корневого блока удобно использовать блок root, который будет отвечать за пробрасывание данных вглубь дерева и создавать page:

block('root').replace()(function() {
    return {
        block: 'page',
        title: 'TODO',
        head: [
            { elem: 'css', url: 'index.min.css' }
        ],
        scripts: [
            { elem: 'js', url: 'index.min.js' }
        ],
        mods: { theme: 'islands' }
    };
});

Попробовать описанный выше подход можно на основе репозитория-заготовки.

С недавнего времени я погружаюсь в БЭМ. И чтоб удостовериться в правильном ли я направлении двигаюсь, хочу попросить более опытных в этом деле ребят сделать небольшое ревью.

В качестве примера для верстки я взял одно старое Яндексовое тестовое задание - https://yandex.ru/jobs/vacancies/dev/dev_des/

Моя реализация - https://github.com/koloskof/bem-taxi

Буду благодарен!

Пытаюсь разобраться с bemtree. Хотел бы использовать решения из bem.info для своего проекта. Скопировал нужные блоки в папку common.blocks. В папке desktop.bundles лежат файлы bemtree и html, но они выглядят как обычные js функции, а не как привычное описание блоков. Потом все это генерится в output-ru Как это использовать? Извиняюсь за бестолковые вопросы. Но больше спросить не у кого.