Open tadatuta opened 8 years ago
Очень не хватает таких туториалов :) Спасибо!
@tadatuta Владимир! Благодарю за описание процесса. Ваш пост и ответ Виталия Харисова представляют общий контекст взаимодействия БЭМ технологий. Но пока не вижу руководства как начать. Поэтому прошу вас дать инструкцию по практическому созданию конкретного блока. Например, блока nav. Куда я должен его прописать, чтобы он прошел все уровни генерации? Допустим у меня есть bundle index и в нем лежит файл index.deps.js котором уже перечислены другие блоки. Есть я пропишу в него "nav" а в bemhtml опишу логику отображения. будет ли этого достаточно? Как мне разместить мой блок и как обеспечить вложенность блоков, чтобы блок nav оказался внутри блока header?
@mathetes вам не надо ничего делать с index.deps.js.
Вам достаточно использовать nav в page и прописать его в page.deps.js, смотрите, как это сделано в bem.info
Получается, что вызов блоков производится в родительских блоках в папке common.blocks. Так я вижу в примере bem.info и заготовке tadatuta. Таким образом формируется структура DOM и не нужно в одном файле писать портянку как это делается вручную при описании bemjson. Правильно я понял?
Да. Каждый блок знает, как сформировать себя из данных (bemtree) и сгенерировать HTML (BEMHTML)
А как разруливать ситуацию если много страничек и на каждой разные данные. В root
городить условия?
@JiLiZART Можно, например, так:
block('root').replace()(function() {
return {
block: 'page',
mods: { view: this.ctx.view } // где view указывает на нужную страницу
};
});
Либо уже в самом page
:
block('page').content()(function() {
return { block: this.data.view }; // при условии, что view пробросили в глобальную data
});
Тогда для каждого типа страницы создается свой собственный блок.
Ну и всякие промежуточные варианты типа:
block('page').content()(function() {
return [
{ block: 'header' },
{ block: this.data.view },
{ block: 'footer' }
];
});
когда использую vm для загрузки .bemtree.js то в контексте можно указать глобальный объект для всех блоков
var context = VM.createContext({
console: console,
Vow: Vow,
...
glob: glob
});
...
block('logo')(
content()(function(){
return [
{
elem:'name'
content: glob.sitename
},
.....
]
})
)
а как сделать по другому ?
@rustam-mh
В моем предыдущем комментарии есть ответ: можно положить необходимые данные в произвольное поле корневого блока, а затем в шаблоне этого блока положить их в this
, тогда данные будут достпны всем потомкам:
var data = require('./path/to/data.json');
require('./bemtree-bundle.js').BEMTREE.apply({ block: 'root', data: data });
block('root').def()(function() {
this.data = this.ctx.data;
return applyNext();
});
block('child').content()(function() {
return this.data;
});
При этом удобно вводить специальный корневой блок именно для пробрасывания данных и не создавать для него отдельный DOM-узел, чтобы при необходимость мочь применять шаблоны для произвольной части дерева. Поэтому можно использовать replace()
, чтобы сразу заменять корневой блок на необходимый:
block('root').replace()(function() {
this.data = this.ctx.data;
return { block: 'page' };
});
Все-таки непонятно как реализовывать страницы сайта, чтоб на каждой были свои блоки, подключались свои стили и скрипты.
С этим ответом ознакомился https://ru.bem.info/forum/716/#comment-158405153 Получается, что различные страницы будут реализованы в рамках одного бандла. В этом бандле мы задаем только один файл *.bemdecl.js
exports.blocks = [{ name: 'root' }];
А уже в root.bemtree.js мы в зависимости от контекста будем указывать модификатор для блока page или указывать для его содержимого определенные блоки. Но как при таком подходе разделять скрипты и стили? Они все соберутся в одном большом бандле, а это не всегда хорошо.
А полагаю, что каждая страница должна быть представлена отдельным бандлом. Ну или не каждая страница, а каждый раздел сайта. Чтоб для этого раздела собирались свои стили и скрипты. А как это правильно сделать?
Правильно ли реализовывать каждую страницу через модификатор блока page? Если страниц будет много, то много будет модификаторов у блока page, будет каша.
Или правильнее каждую страницу реализовывать отдельным блоком?
@v-bornov В зависимости от задачи имеет смысл комбинировать все перечисленные варианты.
В частности в репозитории https://github.com/tadatuta/bem-bemtree-static-project-stub в ветке master
предполагается подход один бандл — одна страница.
В нем же, но в ветке https://github.com/tadatuta/bem-bemtree-static-project-stub/tree/one-bundle-gulp-watcher один общий бандл и генерация страниц на основе массива записей в модели.
А в случае с динамикой может быть совершенно произвольная комбинация того, что (и в какой момент) нужно загрузить на клиент в зависимости от запроса.
block('page').content()(function() {
return [
{ block: 'header' },
{ block: this.data.view },
{ block: 'footer' }
];
});
Это то что нужно. Но для особо одаренных объясните пожалуйста:
{ block: this.data.view }
что это за блок? где он рождается? понятно что это функция - "показать данные", а от куда они придут?{ block: this.data.view }
, а где мне это содержимое закодить?2 и 3 вопрос различается этим - для динамики/для статики
В плане сборки динамика и статика может либо не отличаться вообще, если собирать по bemdecl-у, либо отличаться шагом автоматической генерации декла из bemjson-а. Главное, что нужно понимать, что сборка для динамики должна включать все возможные варианты блоков, которые могут быть использованы в рантайме, т.е. их все следует перечислить в соответствующих bemdecl-ах.
А пример того, как подсунуть данные можно посмотреть в bem-express
: https://github.com/bem/bem-express/blob/master/server/index.js#L67
Хорошо. Попытаюсь по-другому сформулировать.
index.bemdecl.js
в папке - desktop.bundles/index
. Имеет содержимое - exports.blocks = [{ name: 'root' }];
contact.bemdecl.js
в папке - desktop.bundles/contact
. Имеет содержимое - exports.blocks = [{ name: 'root' }];
Планируется:
block('page').content()(function() {
return [
{ block: 'header' },
{ block: 'footer' }
];
});
То есть общее, это { block: 'header' }
, { block: 'footer' }
index.bemdecl.js
имеет:block('content').content() {
{ block: 'video' },
{ block: 'text' },
{ block: 'map' }
});
contact.bemdecl.js
имеет:
block('content').content() {
{ block: 'about' },
{ block: 'map' }
});
Считаю что block('content')
- имеет разное содержимое. Следовательно он и есть - { block: this.data.view }
Следующий код дает понять, что содержимое меняется в зависимости от страницы, но так же существуют общие блоки:
block('page').content()(function() {
return [
{ block: 'header' },
{ block: this.data.view },
{ block: 'footer' }
];
});
Этот код лежит в файле page.bemhtml.js
и является шаблоном блока page
находящийся common.blocks/page/
desktop.bundles/contact/
и desktop.bundles/index/
- какой набор файлов должны содержать, и с каким содержимым?Залил на гитхаб bem-bemtree-static-project-stub с добавленными кастомными блоками, для разбора полетов. Или может ссылкой поделитесь с реализацией, где можно найти решения на данные вопросы!?
Словами, без кода не понятно. Код без слов, тоже малопонятен. Парадокс! )))) Код с разъяснением на примерах понятен. Документация с примерами сразу понимается, если там не абстракция (b_e.ctx и т.д.).
Ну так-то я к предыдущим своим словам привел ссылку на код ;)
Под { block: this.data.view }
подразумевается не то, что там будет один блок с разным контентом, а просто разные блоки. Чтобы не путать их с остальными, мы их именуем с префиксом page-
. Т.е. будут блоки page-index
и page-contact
, которые и опишут разное между страницами.
И именно они будут указаны в соответствующих бандлах помимо root
.
Теперь понятнее или все-таки нужно показать в коде?
PS: обновил bem-bemtree-static-project-stub
, есть смысл апнуться.
bem-bemtree-static-project-stub
обновил. Там появился пример .bem/bemjson.js
логика примерно понятна - entity.block
подменяет значения на нужное.
Даже после просмотра вебинара [Трехзвенная архитектура веб-сервисов на bem-xjst]() структура проекта не понятна.
Ясно что вариативность может быть разная, т.к. это JS.
Но хотя бы рабочий вариант для многостраничника покажите пожалуйста.
Не ясно:
common.bloks/
page-index
page-contact
page-еще 80 блоков разных страниц
это подразумевается?
Или всё таки где-то в bundles
держим эти декларации?
root
- он остается неизменным и его структура переопределяется кодом из вашего примера .bem/bemjson.js
? .bem/bemjson.js
?desktop.bundles/*/*.bemdecl.js
?Лирика: Конечно было бы круто на сайте bem.info иметь пример проекта для bemtree в разделе "учебные материалы", по примеру Создаём свой проект на БЭМ.
cc @tadatuta
Володя пожалуйста помоги с примером реализации.
Честно, я пробовал, перечитал всю документацию, пересмотрел видео. Да, я научился... разобрался немного в ym, даже javascript стал немного понимать (при чтении + пару скриптов написал), писать рабочие шаблоны bemhtml, но не смог реализовать с bemtree требуемое выше.
У нас есть документация про технологии БЭМ платформы (BEMJSON, BEMTREE, BEMHTML, DEPS), но практически нет туториалов по использованию их всех вместе. Есть несколько исключений, но они достаточно объемные и сложноваты для восприятия.
Я написал простой пример, рассчитанный на тех, кто уже немного освоился с версткой на основе project-stub и хочет продолжить свое знакомство с платформой.
Пусть у нас есть следующий файл с данными data.json:
Как вариант, данные могут приходить из БД или через HTTP API — источник не играет роли.
Наша задача сгенерировать на основе этих данных HTML, который будет представлять собой страницу с логотипом в шапке, карточкой пользователя в основной части и копирайтом в подвале.
Первым шагом необходимо из исходных сырых данных получить BEMJSON, описывающий страницу. Для этого будем использовать технологию BEMTREE. При этом договоримся, что в качестве корневого блока, на основе которого будет строиться дерево, возьмем блок
page
.В результате должен получиться следующий BEMJSON:
BEMTREE-шаблон для блока
page
должен построить шапку, основную часть и подвал:По техзаданию в шапке должен быть логотип. Тогда шаблон шапки может выглядеть так:
В основной части нужна карточка пользователя. Так что нам потребуется доступ к данным из файла data.json. Но пока отложим этот момент и захардкодим какие-то тестовые данные:
В подвале нужен копирайт:
Теперь, когда мы знаем, какие потребуются шаблоны, нужно скомпилировать BEMTREE-бандл, который будет включать ядро самого шаблонизатора и код шаблонов.
В самом простом случае мы можем сохранить все шаблоны в один файл, установить пакет bem-xjst и с его помощью скомпилировать бандл:
Но раз мы хотим следовать рекомендации БЭМ методологии и раскладывать каждый шаблон в папку соответствующего блока, то нам потребуется какой-то способ потом эти шаблоны собрать вместе. Для этого подойдет сборщик ENB.
Схема работы
ENB
подробно описана в этом документе. Главное, что нас сейчас интересует — это то, чтоENB
собирает файлы только тех сущностей, которые явно задекларированы.Получить декларацию с перечислением всех нужных блоков можно двумя способами: в
*.bemdecl.js
перечислить все нужные блоки (и не забывать добавлять и удалять их по мере разработки и рефакторинга), либо указать только корневой блок (в нашем случаеpage
), а блоки, которые нужны корневому и всем последующим, указывать в их собственных списках зависимостей — deps.js. Второй путь гораздо гибче: сохраняется прицип БЭМ-методологии о том, что блок сам знает о себе всё, при удалении блока автоматически будут удалены и его зависимости, а при добавлении они автоматически включатся в сборку.Так как шаблон блока
page
создает блокиheader
,main
иfooter
, мы явно укажем это в списке зависимостей в файлеpage.deps.js
:Отлично, теперь мы знаем как собрать шаблоны. Следующим шагом необходимо получить с их помощью BEMJSON на основе данных, а затем из BEMJSON сгенерировать HTML с помощью BEMHTML. В общем виде это выглядит так:
Эти преобразования будут работать и в браузере, если подключить
bundle.bemtree.js
иbundle.bemhtml.js
на страницу. Останется только вставить полученную HTML-строку в DOM.Осталось разобраться, как все-таки сгенерировать карточку пользователя на основе данных из
data.json
, вместо использования хардкода.Как видно в примере кода выше, данные мы передаем в вызов
BEMTREE.apply(data)
. При этом мы помним, что корневым блоком должен оказаться блокpage
. Достичь этого можно следующим образом:Модифицируем код шаблона так, чтобы пробросить данные для вложенных в
page
блоков:Тогда финальный вид BEMTREE-шаблона блока
main
окажется таким:Из соображений унификации в качестве корневого блока удобно использовать блок
root
, который будет отвечать за пробрасывание данных вглубь дерева и создаватьpage
:Попробовать описанный выше подход можно на основе репозитория-заготовки.