bem-site / bem-forum-content-ru

Content BEM forum for Russian speak users
MIT License
56 stars 6 forks source link

Как использовать bemtree из bem.info для своего проекта? #709

Open mathetes opened 8 years ago

mathetes commented 8 years ago

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

voischev commented 8 years ago

@mathetes а почему заинтересовал bemtree именно из bem.info? Может больше ясности внесет этот проект? https://github.com/voischev/express-bemtree-project-stub

qfox commented 8 years ago

Вот что вам нужно: https://github.com/express-bem/project-stub

vithar commented 8 years ago

@voischev @zxqfox то, что вы предлагаете принципиально отличается от того, что у меня в bem.info. У меня генерируется статика, никакого экспресса там нет (и пока не планируется). При этом есть bemtree, генерирующий bemjson и очень простой bemhtml у каждого блока.

vithar commented 8 years ago

@mathetes в desktop.bundles/index надо смотреть только на index.bemdecl.js (его содержимое exports.blocks = [{ name: 'root' }];, остальное генерируется в результате сборки через enb.

Покажите, что у вас сейчас получилось и что именно вызывает вопросы.

voischev commented 8 years ago

@vithar ну я не особо много вижу смысла в bemtree без сервера, есть же bemjson. По этому и предлагаю что могу) Почему bemjson не достаточно в данном случае? Если только генерить контент например из .md файлов... или подобное. Я бы сначала про задачи спросил)

vithar commented 8 years ago

@voischev я рассматриваю bemjson, как генерируемый формат. Не вижу ни одной причины хотеть писать его руками, кроме может простых промо-страниц.

Есть какие-то данные, какая-то структура за bemjson и вот с ней и надо работать, а bemjson это промежуточный формат, чтобы из него просто получать html. Основная логика у меня в bemtree, bemjson генерируется, шаблоны bemhtml получаются простыми.

voischev commented 8 years ago

Понятно что bemhtml простой) Понятно что если "есть какие-то данные" то лучше bemtree. И понятно что bemjson промежуточный :) и удобен только для простых задач.

Я лишь говорю что нужно понять)) — как минимум какой род этих каких то данных))) Ну ладно

qfox commented 8 years ago

@vithar Молодцы! Жаль что года 3 назад до этого не дотумкали ;-(

tadatuta commented 8 years ago

@zxqfox не знаю о чем именно 3 года назад не дотумкали, но коммит с open source реализацией BEMTREE наружу датируется маем 2013, а рассказы о трехзвенной архитектуре и необходимости генерировать BEMJSON были чуть ли не с момента первого анонса BEMHTML ;)

qfox commented 8 years ago

@tadatuta Не, я не про сам bemtree, я про:

У меня генерируется статика, никакого экспресса там нет (и пока не планируется). При этом есть bemtree, генерирующий bemjson и очень простой bemhtml у каждого блока.

Не представляю пока, что вы там за космолет сделали, но очень не хватало процессора для bemtree в рамках bem-tools/enb, чтобы без экспресса запускать и из raw data получать html, как это делают обычные шаблонизаторы ;-)

tadatuta commented 8 years ago

@zxqfox https://github.com/bem/project-stub/commit/5b07cc3a4020b509165cde6f8ba904f5a9341e78 — 14 ноября 2013 года

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

Не представляю пока, что вы там за космолет сделали

Ты так говоришь, как будто BEMTREE.apply(require('./path/to/data.json')).then(function(bemjson) { require('fs').writeFileSync('page.html', BEMHTML.apply(bemjson)); }) — это что-то космическое ;)

qfox commented 8 years ago

@tadatuta Может я не понимаю, но... Разве речь не о:

Хотя стоп, enb же не умеет динамические бандлы? Тогда вы там космос делаете.

Посмотрел ссылку, пойду плакать, все еще не то. Точнее, все еще не до конца то.

tadatuta commented 8 years ago

@zxqfox чего именно не хватает, чтобы было то?

qfox commented 8 years ago

@tadatuta В бандле пишу data.json и bemdecl, оно мне это отправляет в bemtree, ну и дальше по списку. Бекендеры не понимают bemjson ;-)

tadatuta commented 8 years ago

в чем отличие того, что по ссылке, от того, что ты хочешь?

qfox commented 8 years ago

Не то, чтобы я прям очень хочу, но было бы здорово иметь, потому что это снижает порог входа. По ссылке, как бы, хаки какие-то, но очень похоже, да.

mathetes commented 8 years ago

@vithar У меня пока ничего нет. Как я понимаю мне нужно в desktop.bundles в файл index.bemtree.js декларировать блоки. Но в этом файле уже есть много других функций. В какой файл тогда и каким образом мне декларировать блоки?

vithar commented 8 years ago

@mathetes в desktop.bundles/index надо смотреть только на index.bemdecl.js (его содержимое exports.blocks = [{ name: 'root' }];, остальное генерируется в результате сборки через enb.

Дальше надо смотреть в common.blocks/root/root.deps.js. А потом в соответствующие зависимости прописанные в этом блоке.

Каждый блок содержит реализацию себя на всех технологиях и указывает от чего зависит.

mathetes commented 8 years ago

В common.blocks/root/root.deps.js продекларирован только один блок page где остальное? Можете сказать точно куда декларировать все блоки? Чтобы произошла генерация через enb надо сначала продекларировать эти блоки. Вот и вопрос в какой файл? Я пока нашел мало мануала по использованию bemtree Всего одна страница https://ru.bem.info/technology/bemtree/v2/bemtree/ И какие фрагменты в видеосеминарах.

vithar commented 8 years ago

Декларировать все блоки никуда не надо. В root.deps.js написана зависимость от page. В page.deps.js написана зависимость от других блоков, у них от третих. enb собирает это всё, рекурсивно обрабатывая зависимости блоков.

bemtree как BEMHTML, но на выходе генерирует не строки, а объекты, содержащие bemjson. Синтаксис матчей такой же, смотрите у меня примеры, там всё понятно.

mathetes commented 8 years ago

@vithar Хочу уточнить про реализацию в bem-info. Это main-stream как надо работать с bemtree через enb или специфика при работе с данным проектом использующим gulp? Могу ли я использовать данный подход при работе на основе generator-bem-stub? К сожалению пока не могу понять как работать с bemtree. По изученным мануалам нужно описать требуемые на странице блоки и они сгенерятся в bemjson. Еще раз прочитал мануал, который сразу извиняется за то, что не является инструкцией. Посмотрел файлы root.deps.js page.deps.js index.deps.js В них примерно одинаково как я называю "декларируются" различного уровня блоки. В index.deps.js большой список блоков, который вызываются при загрузка страницы index. Или опять не так?

mathetes commented 8 years ago

Сергей Максимов video.yandex.ru/users/ya-events/view/2305/ представляет работу следующим образом декларируешь BemTree он генерит bemjson и выдает html. Это мне понятно. Причем тут еще deps.js не могу понять.

vithar commented 8 years ago

Я не знаю main-stream это или нет, и что вообще можно назвать main-stream'ом.

Я не хочу применять никакую динамику и хочу оставаться в рамках генерации статических файлов при разработке нового bem.info так долго, как это будет возможно. Если где-то будет нужна динамика — буду делать гибрид, когда часть страниц лежит статикой, а только то, что нужно — генерируется динамически. Например, форум точно будет динамическим, он статикой невозможен.

Давай я попробую подробно описать как это всё работает, может кому-то ещё пригодится.

За каждым сайтом стоят какие-то данные из которых нужно генерировать html конкретных страниц. Сайт состоит из страниц, у каждой страницы есть URL, title, и прочие атрибуты.

У меня эта структура описывается в content/pages.js. На самом верхнем уровне идёт разделение по языкам (у меня это ru и en), дальше массив страниц. Каждая страница описывается объектом:

{
    url: 'адрес страницы',
    site: 'подсайт',             // подсайты могут иметь другую навигацию или внешний вид
    title: 'заголовок страницы',
    source: 'где брать контент', // простая строка или
                                 // путь к файлу (начинается с ./) или
                                 // URL (начинается с http)',
    type: 'тип контента' // md – если не указан, используется marked для конвертации в html)
                         // bemjson.js — используется как есть
                         // lib — специальный тип для библиотек, чтобы построить
                         //       меню как на этой странице в шапке
                         //       http://bem.harisov.name/platform/libs/bem-core/2.7.0/
}

Бандл у меня пока один, он лежит в desktop.bundles/index/, файл, который пишется руками там один — desktop.bundles/index/index.bemdecl.js, всё остальное генерируется в процессе сборки через enb.

Его содержимое:

exports.blocks = [{ name: 'root' }];

Во время сборки enb читает этот файл декларации страницы, видит в декларации один блок root. Ищет блок root на всех указанных в конфиге .enb/make.js уровнях.

У меня это

    levels = [
        { path: 'libs/bem-core/common.blocks', check: false },
        { path: 'libs/bem-core/desktop.blocks', check: false },
        { path: 'libs/bem-components/common.blocks', check: false },
        { path: 'libs/bem-components/desktop.blocks', check: false },
        { path: 'libs/bem-components/design/common.blocks', check: false },
        { path: 'libs/bem-components/design/desktop.blocks', check: false },
        'common.blocks'
    ];

Релизация блоков лежит в common.blocks, остальные уровни библиотечные и сейчас мы их не рассматриваем.

Итак, идём в common.blocks/root/root.deps.js и видим, что этот блок зависит от i-bem (лишняя зависимость, надо выкосить) и page:

({
    mustDeps : ['i-bem'],
    shouldDeps : [
        { block : 'page' }
    ]
})

Идём в common.blocks/page/page.deps.js и видим, что этот блок зависит от i-bem (лишняя зависимость, надо выкосить) и блоков, которые являются корневыми на странице:

({
    mustDeps : ['i-bem'],
    shouldDeps : ['header', 'nav', 'promo-header', 'promo-main', 'article', 'footer']
})

Причём на странице может быть или promo-header и promo-main, или article. Можно пойти дальше и разбить на два бандла (promo и content), но меня пока устраивает всё в одном, оптимизацию буду делать потом.

Дальше идём по очереди во все блоки, от которых зависит page и ищем в них deps.js-файлы.

В итоге строится файл desktop.bundles/index/index.deps.js в котором находятся все зависимости всех блоков, необходимых для построения страницы.

Этот файл используется для поиска файлов реализации на всех уровнях переопределения для генерации файлов технологий css, js, bemhtml, bemtree. Эти файлы собираются и складываются desktop.bundles/index.

С технологиями css и js блоков надеюсь всё понятно (если нет — спрашивай), теперь рассмотрим, что происходит в bemtree и bemhtml.

Вызов bemtree происходит в gulpfile.js в applyTemplates:

var bemjson = bemtree.apply({
    block: 'root',
    data: {
        page: page,
        pages: pages[lang],
        langs: langs,
        lang: lang
    }
});

На вход подаётся блок root c данными в поле data. В данных лежит page — текущая страница из sitemap описанного выше, в pages весь sitemap для текущего языка, в langs и lang соответственно массив всех языков и текущий язык.

К блоку root применяется шаблон из common.blocks/root/root.bemtree, там генерируется заголовок страницы в обратном порядке, подготавливаются данные по всем библиотекам и сохраняются в pages, потом генерируется блок page.

К блоку page применяется шаблон из common.blocks/page/page.bemtree. Он возвращает массив из блоков header, nav, вставляет переданный bemjson или генерирует article и footer.

К ним тоже применяются соответствующие bemtree-шаблоны.

После работы всех bemtree-шаблонов возвращается финальное представление страницы в виде bemjson.

Создаём в output соответствующую странице директорию:

var dirName = outputFolder + lang + page.url;

fs.mkdirsSync(dirName);

К bemjson применяем bemhtml и сохраняем в файл index.html соотвествующей директории:

fs.writeFile(dirName + '/index.html', bemhtml.apply(bemjson));

Это позволяет нам автоматически иметь красивые URLи без применения rewrite'ов.

Копируем в эту же директорию файлы из директории, где у нас лежат .md файлы:

var source = page.source;
var type = page.type || 'md';
if (type === 'md' && /^\.\//.test(source)) {
    var sourceDir = source.replace(/[^\/]*$/, '');

    fs.copySync(sourceDir, dirName, { filter: function(file) { return !/md$/.test(file) }});
}

Это позволяет указывать в '.md' относительную ссылку на картинку:

![Декларация](build__declaration.png)

А потом так же относительно использовать её в .html:

<img src="build__declaration.png" alt="Декларация">

bemhtml шаблоны при этом получаются очень простые:

block('breadcrumbs')(
    js()(true),
    tag()('ul'),
    elem('item').tag()('li')
);

Или чуть сложнее:

block('search')(
    js()(true),
    elem('switcher').content()(function() {
        return require('fs').readFileSync('common.blocks/search/search__switcher.svg', 'utf8');
    }),
    elem('input')(
        tag()('input'),
        attrs()(function() {
            var attrs = applyNext() || {};

            attrs.name = 'text';
            attrs.type = 'search';
            attrs.autocomplete = 'off';
            attrs.placeholder = this.ctx.placeholder

            return attrs;
        })
    )
);

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

Надеюсь стало понятнее. Если есть ещё вопросы — спрашивай.

tadatuta commented 8 years ago

Попробовал обобщить схему в посте https://ru.bem.info/forum/716/

mathetes commented 8 years ago

Спасибо за подробное описание процесса. Очень нужно. Стало понятнее, но не до конца. Еще Владимир Гриненко сделал отдельный пост для меня. Его тоже штудирую. По поводу main-stream хотелось бы понять пример работы с БЭМ на фул-стэке. Каким образом определяется структура DOM дерева в процессе? То есть как определяется вложенность блоков у страницы? Как получается, что на bem.info один и тот же bundle генерит несколько страниц? Я думал тут динамическая генерация, оказывается статическая согласно вашего утверждения. Не понимаю тогда. Судя по последнему посту тут задействован pages.js. Продолжаю штудировать последний пост. Нужно время переварить все это. Еще раз спасибо за консультацию.