// TOC | |
// Intro | |
// # New BEM based blocks concept — BEMS | |
// # New 3-step templating concept | |
// Example | |
/** | |
# New BEM based blocks concept — BEMS | |
## Нейминг: Не мода, а поле | |
Вместо термина "мода" (mode), используется "поле" (prop) | |
Ибо, какая нафиг мода (=режим), если это просто поле? | |
## mods | |
Модификаторы расщепляются на mods (view) и state | |
## mix | |
Миксы расщепляются на mix и base (наследование) | |
*/ | |
/** | |
# New 3-step templating concept (data-data-bemjson-html) | |
bempriv is optional. Uses for complex blocks | |
[BEMPRIV] XJT BEMHTML | |
-- data ---------@-- data ------@-- bemjson --@-- html | |
fp js code --/ xjt tpls --/ bemhtml --/ | |
instead of old model: | |
[BEMPRIV] BEMTREE BEMHTML | |
-- data ---------@-- bemjson --@--- bemjson --@-- html | |
js code --/ bemtree --/ bemhtml --/ | |
Clear Data aren't contain `{ block: 'foo' }` | |
xjt - Data JSON to BEMJSON transition tool (concept) | |
## Нейминг: не ctx, а data | |
## Доопределения и переопределения | |
Merge strategies | |
merge: 'override', // by entire propSet - переопределение | |
merge: 'extend', // by entire props (default) - доопределение, переопределяющее имеющиеся поля | |
merge: 'deep', // deep props scan, not workin' on arrays - глубокое доопределение | |
Note: Массивы не мержатся (На массивах всегда выставляется стратегия extend) | |
*/ | |
// Example | |
// common: | |
[{ | |
// match section (block, elem, mods, elemMods) | |
block: 'carousel', | |
// apply (body) section (state, content, mix) | |
state: { offset: 'start' }, // start|middle|end | |
// gives -> [{elem:'item'..},{elem:'item'..},{elem:'item'..},,,] | |
// .items <- this.data.items = [{thumb:{},title,,},,,] | |
content: { mapData: '.items', elem: 'item' }, | |
}, | |
{ | |
block: 'carousel', elem: 'item', | |
state: { theme: 'square' }, | |
url: '.thumb.url', | |
title: '.title' // original data: this.data.items[i].title | |
}] | |
// deskpad: | |
[{ | |
merge: 'override', // not recomended at all | |
block: 'carousel', elem: 'item', | |
state: { hovered: false }, // after override: { hovered: false } (loose theme: 'square' ) | |
}] | |
// desktop: | |
[{ | |
merge: 'deep', | |
// matches `carousel__has-header` only | |
block: 'carousel', mods: { 'has-header': true }, | |
content: [ { elem: 'header' }, '::content' ] // access super block prop | |
}, | |
{ | |
merge: 'deep', | |
block: 'carousel', elem: 'item', | |
state: { theme: 'rect' }, // after merge: { full: true, theme: 'rect' } | |
content: '.title' | |
}] |
В реальном проекте код bemhtml часто становится излишне сложным и нечитаемым с обилием имеративщины
Но куда больше у нас есть плохих привов, с обилием present'тативщины
Сложно не нарушать баланс данные/представление (то и дело всё скатывается в PHP)
И без имеративных конструкций не обходится
Возникает и такая проблема: Если полностью вынести представление из (bem)priv,
то в bemhtml (или в bemtree) придётся написать map'ы, а это при всей декларативнсти FP всё-равно too much императивно для декларативного шаблона
Детали:
Некоторые проблемы bempriv-bemtree-bemhtml
- в привах и маппинг данных и накладывание view
- в bemhtml/bemtree обилие иперативщины
- ...map(...,function(){...})
- applyNext(function(){this.mods || ...})
- this.ctx.foo.push(...))
Проблема: Доопределения и переопределения
{ someProp: '::someProp' } // access super prop as is
{ someProp: ['::someProp', 'foo', 'bar'] } // access super prop to embeding
{ someProp: function(baseProp, ctx){} } // complex access super prop - not recomended
Merge strategies
merge: 'override', // by entire propSet
merge: 'extend', // by entire props
merge: 'deep', // deep props scan, not workin' on arrays
merge: 'inherit', // inherit parent propSet strategy or default
merge: 'default', // = 'extend'
На массивах всегда выставляется стратегия extend
NOTE: Не следует на js писать доопределение встраивание внутрь массива,
Вместо этого поместить в массив заглушки,
которые уже переопределять.
Фича: Сквозные поля для блоков
this.blockData (= this.data блока, к которому относятся элементы)
Сахар: Блоки-Алиасы
bemhtml
block('share-dropdown').replace()({ block: 'dropdown', popup: 'share' })
xjt
{ block: 'share-dropdown', is: { block: 'dropdown', popop: 'share' } }
Синтаксис: Маппинг данных на блоки и элементы
{ mapData: '.key1.nestedKey2', elem: 'el' }
Результат:
// [{elem:'el',,},{elem:'el',,},{elem:'el'},,,]
Концептуальная проблема: теряется гибкость?
Q: раньше можно было где угодно в данных (в BEMPRIV/vanilla.node.js) выставить поле представления (block/elem)
и включить шаблонизацию
A: Да и теперь можно в XJT, и более декларативно.
BEMPRIV тоже можно использовать между бекендом и XJT для слодных блоков, но не обязательно
mix или наследование?
И о и другое
mix
блок foo-bar в content'е блока qux-pux, миксуется на элемент foo этого родительского блока
{ mix: [{ block: 'qux-pux', elem: 'foo' }] } // or mix: ['qux-pux__foo']
base
блок foo-bar наследует блок bar
{ block: 'foo-bar', base: { block: 'bar'} } // or base: 'bar'
Q: Как отлаживать? Мы только отказались от bemhtml DSL
A: в любом месте XJT-шаблона предполагается возможным заменить короткую запись на функцию и поставить туда console.log или debugger, а затем вернуть обратно декларативную запись.
Кроме того, есть идея использовать спец-символ в строках типа '.key' для включения отладки
например: '!log!.key'
будет выводить подробный лог проходящих через маппинг данных со всеми "явками и паролями"
Небольшое сравнение читаемости синтаксисов BEM-XJST и XJT