Миграция
4.0.0
Изменения в блоке i-bem
Отдельный блок i-bem-dom
Элемент dom
блока i-bem
был перенесён в отдельный блок i-bem-dom
.
Было:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
/* ... */
});
Стало:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
/* ... */
});
Блоки i-bem
и i-bem-dom
больше не являются классами, представляя собой модули с методами для декларации
БЭМ-сущностей, ссылками на классы БЭМ-сущностей и некоторыми дополнительными хелперами. Эти методы больше не являются методами класса для соответсвующих блоков.
Задача: #413.
Декларация
Декларация блока
Для декларации блока, вместо метода decl()
, следует использовать метод declBlock()
.
Было:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }));
});
Стало:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }));
});
Декларация модификатора
Для декларации модификатора, вместо статического метода decl()
, следует использовать статический метод declMod()
.
Было:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));
});
Стало:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'myVal' }, { /* ... */ }));
});
Декларация булевого модификатора
Было:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'true' }, { /* ... */ }));
});
Стало:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod' }, { /* ... */ }));
});
Задача: #1374.
Декларация для модификатора с любым значением
Было:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ modName : 'my-mod' }, { /* ... */ }));
});
Стало:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod', modVal : '*' }, { /* ... */ }));
});
Задача: #1376.
Доопределение блока
Вместо метода decl()
класса блока следует использовать метод declBlock()
модуля i-bem-dom
.
Было:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ /* ... */ }));
});
Стало:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom, MyDomBlock) {
provide(bemDom.declBlock(MyDomBlock, { /* ... */ }));
});
Декларация наследуемого блока
Было:
modules.define('my-dom-block', ['i-bem__dom', 'my-base-dom-block'], function(provide, BEMDOM, MyBaseDomBlock) {
provide(BEMDOM.decl({ block : this.name, baseBlock : MyBaseDomBlock }, { /* ... */ }));
});
Стало:
modules.define('my-dom-block', ['i-bem-dom', 'my-base-dom-block'], function(provide, bemDom, MyBaseDomBlock) {
provide(bemDom.declBlock(this.name, MyBaseDomBlock, { /* ... */ }));
});
Декларация миксина
Метод declMix
переименован в declMixin
, чтобы отделить понятие
миксов нескольких БЭМ-сущностей на одном DOM-узле от миксинов на уровне JS.
Было:
modules.define('my-mix-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.declMix(this.name, { /* ... */ }));
});
Стало:
modules.define('my-mixin-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declMixin({ /* ... */ }));
});
Примешивание миксина
Было:
modules.define('my-dom-block', ['i-bem__dom', 'my-mix-1', 'my-mix-2'], function(provide, BEMDOM) {
provide(BEMDOM.decl({ block : this.name, baseMix : ['my-mix-1', 'my-mix-2']}, { /* ... */ }));
});
Стало:
modules.define('my-dom-block', ['i-bem-dom', 'my-mixin-1', 'my-mixin-2'], function(provide, bemDom, MyMixin1, MyMixin2) {
provide(bemDom.declBlock(this.name, [MyMixin1, MyMixin2], { /* ... */ }));
});
Триггеры для изменения модификаторов
При декларации определённого модификатора (например, _my-mod_my-val
) невозможно было задекларировать поведение
на удаление этого модификатора. Приходилось делать две декларации.
Было:
// my-dom-block_my-mod_my-val.js
modules.define('my-dom-block', function(provide, MyDomBlock) {
MyDomBlock.decl({
onSetMod : {
'my-mod' : {
'' : function() { /* ... */ } // декларация для удаления модификатора _my-mod_my-val
}
}
});
provide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));
});
Стало:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'my-val' }, {
onSetMod : {
'mod1' : {
'' : function() { /* ... */ } // декларация для удаления модификатора _my-mod_my-val
}
}
}));
});
Задача: #1025.
Появился сокращённый синтаксис для декларации поведения на изменение модификатора.
Было:
onSetMod : {
'my-mod' : {
'*' : function(modName, modVal, prevModVal) {
if(prevModVal === 'my-val') {
/* ... */ // декларация для изменения _my-mod_my-val в любое другое значение
}
}
}
}
Стало:
onSetMod : {
'my-mod' : {
'~my-val' : function() { /* ... */ } // декларация для изменения значения my-mod из my-val в любое другое значение
}
}
}
Было:
onSetMod : {
'my-mod' : {
'*' : function(modName, modVal) {
if(modVal !== 'my-val') {
/* ... */ // декларация для изменения my-mod в любое значение, кроме my-val
}
}
}
}
Стало:
onSetMod : {
'my-mod' : {
'!my-val' : function() { /* ... */ } // декларация для изменения my-mod в любое значение, кроме my-val
}
}
}
Задача: #1072.
Ленивая инициализация
Функциональность поля live
была разделена на две части: поле lazyInit
и метод onInit()
.
Было:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : true
}));
});
Стало:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
lazyInit : true
}));
});
Было:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : function() {
/* ... */
}
}));
});
Стало:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
lazyInit : true,
onInit : function() {
/* ... */
}
}));
});
Было:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : function() {
/* ... */
return false;
}
}));
});
Стало:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
onInit : function() {
/* ... */
}
}));
});
Было:
{
block : 'b1',
js : { live : false }
}
Стало:
{
block : 'b1',
js : { lazyInit : false }
}
Задача: #877.
Экземпляры для элементов
Удалены элемент elem-instances
блока i-bem
и модификатор elem-instances
элемента dom
блока i-bem
.
Теперь соответствующая функциональность является частью i-bem
и i-bem-dom
.
Было:
modules.define('my-dom-block__my-elem', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl({ block : 'my-dom-block', elem : 'my-elem' }, { /* ... */ }));
});
Стало:
modules.define('my-dom-block__my-elem', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declElem('my-dom-block', 'my-elem', { /* ... */ }));
});
Теперь метод _elem(elemName)
экземпляра блока (бывший elem(elemName)
) возвращает не jQuery-объект со всеми элементами
с именем elemName
, а экземпляр класса элемента.
Для того, чтобы получить коллекцию экземпляров класса элемента, используйте метод _elems()
.
Не забудьте включить поддержку экземплятор для элементов в шаблонизаторе.
Опция elemJsInstances для bem-xjst
или jsElem для BH
.
Cпособы взаимодействия с элементами
Было:
this.setMod(this.elem('my-elem'), 'my-mod', 'my-val');
Стало:
this._elem('my-elem').setMod('my-mod', 'my-val');
Аналогично для методов getMod()
, hasMod()
, toggleMod()
, delMod()
.
Удалённые методы и поля
Удалён метод dropElemCache()
, теперь инвалидация кэшей осуществляется автоматически при модификациях DOM. Задача: #1352.
Из API блока удалены методы: elemify()
, elemParams()
и поле onElemSetMod
. Соответствующая
им функциональность выражается через экземпляры элементов .
См. также изменения про методы поиска.
Задача: #581.
Методы поиска
Переименованы следующие методы:
findBlockInside()
вfindChildBlock()
findBlocksInside()
вfindChildBlocks()
findBlockOutside()
вfindParentBlock()
findBlocksOutside()
вfindParentBlocks()
findBlockOn()
вfindMixedBlock()
findBlocksOn()
вfindMixedBlocks()
Из этих методов удален опциональный первый параметр про элемент.
Добавлены методы: findChildElem()
, findChildElems()
, findParentElem()
, findParentElems()
, findMixedElem()
, findMixedElems()
.
Было:
this.findBlockInside(this.elem('my-elem'), 'my-block-2');
Стало:
this.findChildElem('my-elem').findChildBlock(MyBlock2);
Удалены методы: findElem()
, closestElem()
, вместо них следует использовать методы findChildElem()
и findParentElem()
, соответсвенно.
Методы findChildBlocks()
, findParentBlocks()
, findMixedBlocks()
, findChildElems()
, findParentElems()
,
findMixedElems()
возвращают коллекции БЭМ-сущностей.
Методы findChildElem()
и findChildElems()
(в отличие от предыдущего аналога findElem
) не выполняют поиск на собственных DOM-узлах экземпляра.
Было:
this.findElem('my-elem');
Стало:
this.findChildElems('my-elem').concat(this.findMixedElems('my-elem'));
Но рекомендуется обратить внимание, действительно ли необходимы оба поиска:
в большинстве случаев достаточно использовать или this.findChildElems('my-elem')
или this.findMixedElems('my-elem')
.
Проверка вложенности
Вместо удаленного метода containsDomElem()
, следует использовать метод containsEntity()
.
Было:
this.containsDomElem(someElem);
Стало:
this.containsEntity(someElem);
Коллекции
Функциональность элемента collection
блока i-bem
перестала быть опциональной.
Все методы возвращавшие массив БЭМ-сущностей, теперь возвращают коллекции.
Было:
this.findBlocksInside('my-block-2')[0].setMod('my-mod', 'my-val');
Стало:
this.findChildBlocks(MyBlock2).get(0).setMod('my-mod', 'my-val');
Было:
this.findBlocksInside('my-block-2').forEach(function(myBlock2) {
return myBlock2.setMod('my-mod', 'my-val');
});
Стало:
this.findChildBlocks(MyBlock2).setMod('my-mod', 'my-val');
Задача: #582.
События
API работы с событиями значильно упрощено. Удалены методы экземпляра блока: on()
, un()
, once()
, bindTo()
,
unbindFrom()
, bindToDoc()
, bindToWin()
, unbindFromDoc()
, unbindFromWin()
и методы класса: liveBindTo()
,
liveUnbindFrom()
, on()
, un()
, once()
, liveInitOnBlockEvent()
, liveInitOnBlockInsideEvent()
.
Вместо них добавлены методы _domEvents()
и _events()
, возвращающие экземпляр класса менеджера событий, с методами
on()
, un()
и once()
;
DOM-события на экземплярах
Было:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.bindTo('click', this._onClick);
}
}
}
});
Стало:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._domEvents().on('click', this._onClick);
}
}
}
});
Было:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.bindToDoc('click', this._onDocClick);
}
}
}
});
Стало:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._domEvents(bemDom.doc).on('click', this._onDocClick);
}
}
}
});
Было:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.bindToWin('resize', this._onWinResize);
}
}
}
});
Стало:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._domEvents(bemDom.win).on('resize', this._onWinResize);
}
}
}
});
Ссылка на экземпляр
Если событие произошло на БЭМ-экземпляре, в объект события будет добавлено поле, ссылающееся на экземпляр:
this._domEvents('my-elem').on('click', function(e) {
e.bemTarget // ссылается на экземпляр `my-elem`
});
БЭМ-события на экземплярах
Было:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.findBlockOutside('my-block-2').on('my-event', this._onMyBlock2MyEvent, this);
},
'' : function() {
this.findBlockOutside('my-block-2').un('my-event', this._onMyBlock2MyEvent, this);
}
}
}
});
Стало:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._events(this.findParentBlock('my-block-2')).on('my-event', this._onMyBlock2MyEvent);
}
}
}
});
Следует обратить внимание, что теперь, отписка от событий происходит автоматически во время уничтожения экземпляра.
Делегированные DOM-события
Было:
BEMDOM.decl('my-block', { /* ... */ }, {
live : function() {
this.liveBindTo('click', this.prototype._onClick);
}
});
Стало:
bemDom.declBlock('my-block', { /* ... */ }, {
onInit : function() {
this._domEvents().on('click', this.prototype._onClick);
}
});
Было:
BEMDOM.decl('my-block', { /* ... */ }, {
live : function() {
this.liveBindTo('my-elem', 'click', this.prototype._onMyElemClick);
}
});
Стало:
bemDom.declBlock('my-block', { /* ... */ }, {
onInit : function() {
this._domEvents('my-elem').on('click', this.prototype._onMyElemClick);
}
});
Делегированные БЭМ-события
Было:
BEMDOM.decl('my-block', { /* ... */ }, {
live : function() {
this.liveInitOnBlockInsideEvent('my-event', 'my-block-2', this.prototype._onMyBlock2MyEvent);
}
});
Стало:
bemDom.declBlock('my-block', { /* ... */ }, {
onInit : function() {
this._events(MyBlock2).on('my-event', this.prototype._onMyBlock2MyEvent);
}
});
Следует обратить внимание, что параметр с функцией обработчиком события теперь обязательный.
Было:
modules.define('my-block', ['i-bem__dom', 'my-block-2'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : function() {
this.liveInitOnBlockInsideEvent('my-event', 'my-block-2');
}
}));
});
Стало:
modules.define('my-block', ['i-bem-dom', 'my-block-2', 'functions'], function(provide, bemDom, MyBlock2, functions) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
onInit : function() {
this._events(MyBlock2).on('my-event', functions.noop);
}
}));
});
Было:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
MyBlock2.on(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);
},
'' : function() {
MyBlock2.un(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);
}
}
}
});
Стало:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._events(MyBlock2).on('my-event', this._onMyBlock2MyEvent);
}
}
}
});
Следует обратить внимание, что теперь, отписка от событий происходит автоматически во время уничтожения экземпляра.
Взаимодействие стороннего кода с БЭМ-блоками
Получение экземпляра БЭМ-блока
Теперь, метод jQuery-объекта bem()
принимает БЭМ-класс, вместо строки.
Было:
modules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {
var myBlock = $('.my-block').bem('my-block');
});
Стало:
modules.require(['jquery', 'my-block'], function($, MyBlock) {
var myBlock = $('.my-block').bem(MyBlock);
});
Подписка на БЭМ-события из стороннего кода
Было:
modules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {
$('.my-block').bem('my-block').on('my-event', function() { /* ... */ });
});
Стало:
modules.require(['jquery', 'my-block', 'events__observable'], function($, MyBlock, observable) {
observable($('.my-block').bem(MyBlock))
.on('my-event', function() { /* ... */ });
});
При этом в зависимости нужно добавить { block : 'events', elem : 'observable', mods : { type : 'bem-dom' } }
.
Задача: #394.
Имена protected-методов начинаются с _
Переименованы protected-методы:
emit()
в_emit()
elem()
в_elem()
buildClass()
в_buildClassName()
buildSelector()
в_buildSelector()
getDefaultParams()
в_getDefaultParams()
Удалённые методы
Удалён метод getMods()
.
Изменения в блоке querystring
Элемент querystring__uri
стал блоком uri
. Блок querystring
стал элементом uri__querystring
.
Задача: #967.
Изменения в блоке page
Элемент page__css
больше не поддреживает поле ie
. Используйте элемент page__conditional-comment
.
Было:
{
block : 'page',
head : [
{ elem : 'css', url : 'my-css.css', ie : false },
{ elem : 'css', url : 'my-css', ie : true }
],
content : 'Page content'
}
Стало:
{
block : 'page',
head : [
{
elem : 'conditional-comment',
condition : '! IE',
content : { elem : 'css', url : 'my-css.css' }
},
{
elem : 'conditional-comment',
condition : '> IE 8',
content : { elem : 'css', url : 'my-css.ie.css' }
}
// и т.д. для других нужных версий IE
],
content : 'Page content'
}
Задача: #379.
3.0.0
Для миграции на версию 3.0.0 достаточно ознакомиться с историей изменений.
2.0.0
Для миграции на версию 2.0.0 достаточно ознакомиться с историей изменений.
1.0.0
Для версии 1.0.0 миграция подразумевается с использования bem-bl на использование bem-core.
Модули
Весь код теперь пишется в терминах модульной системы https://github.com/ymaps/modules. Все зависимости должны явно указываться в коде, обращения к глобальным объектам необходимо минимизировать, а, по возможности, и полностью исключить.
Пример:
modules.define(
'my-module', // имя модуля
['module-from-library', 'my-another-module'], // зависимости модуля
function(provide, moduleFromLibrary, myAnotherModule) { // декларация модуля, вызывается когда все зависимости "разрезолвлены"
// предоставление модуля
provide({
myModuleMethod : function() {}
});
});
TODO: дописать про изменение сборки (использование специальных технологий для js и как быть с кастомными сборщиками)
jQuery и jQuery-плагины
jQuery представлен модулем-оберткой jquery
, который использует глобальный объект jQuery,
в случае если он уже присутствует на странице, в противном случае загружая его самостоятельно.
jQuery теперь используется только для операций, связанных непосредственно с DOM
(поиск элементов, подписка на события, установка/получение атрибутов элементов, и т.д.).
Для всех остальных операций написаны соответствующие модули, предоставляющие аналогичный функционал, но, при этом, не зависящие от jQuery:
модуль
objects
для работы с объектами (с методамиextend
,isEmpty
,each
)модуль
functions
для работы с функциями (с методамиisFunction
иnoop
)
Также, все jQuery-плагины, не связанные непосредственно с jQuery
($.observable
, $.inherit
, $.cookie
, $.identify
, $.throttle
) стали модулями:
модуль
events
вместо$.observable
для работы с событиями, предоставляющий "классы"EventsEmitter
иEvent
модуль
inherit
вместо$.inherit
для работы с "классами" и наследованиеммодуль
cookie
вместо$.cookie
модуль
identify
вместо$.identify
модули
functions__throttle
,functions__debounce
вместо$.throttle
и$.debounce
, соответственно
Было:
// код блока
$.throttle()
// код блока
Стало:
module.define('my-module', ['functions__throttle'], function(provide, throttle) {
// код модуля
throttle()
// код модуля
});
BEM.DOM-блоки
Декларация
Вместо декларации через BEM.DOM.decl необходимо доопределять модуль i-bem__dom
.
Было:
BEM.DOM.decl('block', /* ... */);
Стало:
modules.define('i-bem__dom', function(provide, BEMDOM) {
BEMDOM.decl('block', /* ... */);
provide(BEMDOM);
});
Конструктор
Необходимо использовать полную нотацию для обработчика установки модификатора js
в значение inited
.
Было:
onSetMod : {
js : function() {
// код конструктора
}
}
Стало:
onSetMod : {
'js' : {
'inited' : function() {
// код конструктора
}
}
}
Деструктор
Вместо метода destruct
необходимо использовать обработчик установки модификатора js
в пустое значение (удаление модификатора).
Вызывать __base
для того, чтобы у блоков работал базовый деструктор, определенный в i-bem__dom
, больше не нужно.
Было:
destruct : function() {
this.__base.apply(this, arguments);
// код деструктора
}
Стало:
onSetMod : {
js : {
'' : function() {
// код деструктора
}
}
}
Метод changeThis
Вместо метода changeThis
необходимо использовать либо соответствующий параметр, либо нативный метод bind
, если такой параметр отсутствует.
Было:
// код блока
obj.on('event', this.changeThis(this._method));
// код блока
Стало:
obj.on('event', this._method.bind(this));
// или лучше
obj.on('event', this._method, this);
Метод afterCurrentEvent
Вместо метода afterCurrentEvent
необходимо использовать метод nextTick
,
который гарантирует, что блок еще существует в момент исполнения callback'а
(если блок уже уничтожен к этому моменту, то callback не исполняется).
Было:
BEM.DOM.decl('block', {
method : function() {
this.afterCurrentEvent(function() {
/* ... */
});
}
});
Стало:
modules.define('i-bem__dom', function(provide, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
this.nextTick(function() {
/* ... */
});
}
});
});
Метод findElem
Контекст для поиска элемента больше не задается строкой, вместо нее следует передавать jQuery-объект.
Было:
var nestedElem = this.findElem('parent-elem', 'nested-elem');
Стало:
var nestedElem = this.findElem(this.findElem('parent-elem'), 'nested-elem'),
oneMoreElem = this.findElem(this.elem('another-elem'), 'nested-elem');
Метод liveBindTo
Метод liveBindTo
больше не поддерживает поле elemName
для передачи имени элемента. Вместо него следует использовать поле elem
.
Доступ до DOM-элемента в обработчике события
DOM-элемент, к которому был подвешен обработчик события теперь доступен
как $(e.currentTarget)
вместо e.data.domElem
.
Было:
onClick : function(e) {
e.data.domElem.attr(/* ... */);
}
Стало:
onClick : function(e) {
$(e.currentTarget).attr(/* ... */);
}
Каналы (channels)
Каналы больше не являются встроенными в BEM, теперь они являются самостоятельным модулем events__channels
.
Было:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('channel-name').on(/* ... */);
}
});
Стало:
modules.define('i-bem__dom', ['events__channels'], function(provide, channels, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
channels('channel-name').on(/* ... */);
}
});
});
Блок i-system
и канал sys
событий tick
, idle
, wakeup
Этот блок и канал перестали существовать, вместо них появились отдельные модули: tick
с событием tick и idle
с событиями idle и wakeup.
Было:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('tick', /* ... */);
}
});
Стало:
modules.define('i-bem__dom', ['tick'], function(provide, tick, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
tick.on('tick', /* ... */);
}
});
});
Было:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('wakeup', /* ... */);
}
});
Стало:
modules.define('i-bem__dom', ['idle'], function(provide, idle, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
idle.on('wakeup', /* ... */);
}
});
});
BEM-блоки
Те BEM-блоки, которые использовались как хранилище для каких-то методов, при этом никак не использующие BEM-методологию, теперь могут быть написаны как модули.
Было:
BEM.decl('i-router', {
route : function() { /* ... */ }
});
Стало:
modules.define('router', function(provide) {
provide({
route : function() { /* ... */ }
});
});
Если же, по каким-то причинам, нужны именно BEM-блоки (не BEM.DOM-блоки), то их можно объявлять, доопределяя модуль i-bem
.
Было:
BEM.decl('my-block', { /* ... */ });
Стало:
modules.define('i-bem', function(provide, BEM) {
BEM.decl('my-block', { /* ... */ });
provide(BEM);
});
Рефакторинг на примере блока b-spin
Было:
BEM.DOM.decl('b-spin', {
onSetMod : {
'js' : function() {
this._size = this.getMod('size') || /[\d]+/.exec(this.getMod('theme'))[0];
this._bgProp = 'background-position';
this._posPrefix = '0 -';
if (this.elem('icon').css('background-position-y')) { /* В IE нельзя получить свойство background-position, только background-position-y, поэтому костыляем */
this._bgProp = 'background-position-y';
this._posPrefix = '-';
}
this._curFrame = 0;
this.hasMod('progress') && this.channel('sys').on('tick', this._onTick, this);
},
'progress' : {
'yes' : function() {
this.channel('sys').on('tick', this._onTick, this);
},
'' : function() {
this.channel('sys').un('tick', this._onTick, this);
}
}
},
_onTick: function(){
var y = ++this._curFrame * this._size;
(y >= this._size * 36) && (this._curFrame = y = 0);
this.elem('icon').css(this._bgProp, this._posPrefix + y +'px');
},
destruct : function() {
this.channel('sys').un('tick', this._onTick, this);
this.__base.apply(this, arguments);
}
});
Стало:
modules.define(
'i-bem__dom',
['tick'],
function(provide, tick, BEMDOM) {
var FRAME_COUNT = 36;
BEMDOM.decl('b-spin', {
onSetMod : {
'js' : {
'inited' : function() { // конструктор
var hasBackgroundPositionY = !!this.elem('icon').css('background-position-y')); /* В IE нельзя получить свойство background-position, только background-position-y */
this._bgProp = hasBackgroundPositionY? 'background-position-y' : 'background-position';
this._posPrefix = hasBackgroundPositionY? '-' : '0 -';
this._curFrame = 0;
this._size = Number(this.getMod('size') || /[\d]+/.exec(this.getMod('theme'))[0]);
this.hasMod('progress') && this._bindToTick();
},
'' : function() { // деструктор
this._unbindFromTick();
}
},
'progress' : {
'true' : function() {
this._bindToTick();
},
'' : function() {
this._unbindFromTick();
}
}
},
_bindToTick : function() {
tick.on('tick', this._onTick, this);
},
_unbindFromTick : function() {
tick.un('tick', this._onTick, this);
},
_onTick : function() {
var offset;
this._curFrame++ >= FRAME_COUNT?
offset = this._curFrame * this._size :
this._curFrame = offset = 0;
this.elem('icon').css(this._bgProp, this._posPrefix + offset + 'px');
}
});
provide(BEMDOM);
});