EN RU
Форум

Методология

Технологии

Инструментарий

Библиотеки

Учебные материалы

Миграция

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.

Методы поиска

Переименованы следующие методы:

Из этих методов удален опциональный первый параметр про элемент.

Добавлены методы: 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-методы:

Задачи: #586, #1359.

Удалённые методы

Удалён метод 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:

Также, все jQuery-плагины, не связанные непосредственно с jQuery ($.observable, $.inherit, $.cookie, $.identify, $.throttle) стали модулями:

Было:

// код блока
$.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);

});