Migration
1.0.0
For 1.0.0 version we assume migration from bem-bl to bem-core.
Modules
From now everything should be under the ym modular system. All the dependencies have to be mentioned in the code, using global variables have to be minimized to 0 if possible.
Example
modules.define(
'my-module', // Module name
['module-from-library', 'my-another-module'], // Module dependencies
function(provide, moduleFromLibrary, myAnotherModule) { // Module declaration, runs when all the dependencies are resolved
//Module providing
provide({
myModuleMethod : function() {}
});
});
TODO: add information about changes in build process (usage of special techs for js and instructions for custom builders).
jQuery and plugins
jQuery is represented with a wrapping module jquery which uses the jQuery
global object if it is available or loads jQuery additionally.
From now jQuery is used only for operations on DOM such as selecting nodes,
binding listeners to events, getting and setting attribute values and so on.
For other operations there are special modules non-dependable on jQuery.
the
objectsmodule to operate on objects (withextend,isEmptyandeachmethods) *thefunctionsmodule to operate on functions (withisFunctionandnoopmethods)
All the jQuery plugins which are not fo DOM operation became modules:
the
eventsmodule used to be the$.observablejQuery pluginIt works with events, provides "classes"EventsEmitterandEventthe
inheritmodule used to be the$.inheritpluginIt provides an inherit module with classes.the
cookiemodule used to be the$.cookiepluginthe
identifymodule used to be$.identifypluginthe
functions__throttleandfunctions__debounceused to be the$.throttleand the$.debounceplugins
Before:
// block code
$.throttle(...
// block code
After:
module.define('my-module', ['functions__throttle'], function(provide, throttle) {
// module code
throttle(...
// module code
BEM.DOM blocks
Declaration
Blocks represented in DOM were declared with BEM.DOM.decl. Now they must use
i-bem__dom module and extend it.
Before:
BEM.DOM.decl('block', ...);
After:
modules.define('i-bem__dom', function(provide, BEMDOM) {
BEMDOM.decl('block', ...);
provide(BEMDOM);
});
Constructor
You have to use full notation for the callback for the js modifier in its
inited value.
Before:
onSetMod : {
js : function() {
// constructor code
After:
onSetMod : {
js : {
inited : function() {
// constructor code
Destructor
Instead of destruct method the destructive callback has to be applyed to the
empty value of js modifier, which corresponds removing a modifier from a
block.
Also you do not need to call __base to run a descructor from the basic
i-bem__dom module.
Before:
destruct : function() {
this.__base.apply(this, arguments);
// destructor code
After:
onSetMod : {
js : {
'' : function() {
// destructor code
The changeThis method
Instead of changeThis method you have to use native bind.
Before:
// block code
obj.on('event', this.changeThis(this._method);
// block code
After:
obj.on('event', this._method.bind(this));
// or better
obj.on('event', this._method, this);
The afterCurrentEvent method
Use the nextTick method instead of afterCurrentEvent. The nextTick assures
that the block exists at the time of running a callback. If the block is already
destructed, the callback will not be run.
Before:
BEM.DOM.decl('block', {
method : function() {
this.afterCurrentEvent(function() { ...
After:
modules.define('i-bem__dom', function(provide, DOM) {
DOM.decl('block', {
method : function() {
this.nextTick(function() { ...
Access to a DOM element from an event handler callback
The callback binded to a DOM element as an event handler is now provided with
the link to this DOM element as $(e.currentTarget) instead of e.data.domElem.
Before:
onClick : function(e) {
e.data.domElem.attr(...
After:
onClick : function(e) {
$(e.currentTarget).attr(...
NB Remember that jQuery is unavailable in global scope and you must use jquery module for access to it.
Channels
Channels are not embedded into BEM any more. Now they are the separate
events__channels module.
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('channel-name').on(....
After:
modules.define('i-bem__dom', ['events__channels'], function(provide, channels, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
channels('channel-name').on(....
The i-system block, the sys channel and the tick, idle and wakeup events
The is no i-system block any more. Instead you can use special modules:
tick with the tick event and idle with the events idle and wakeup.
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('tick', ...
After:
modules.define('i-bem__dom', ['tick'], function(provide, tick, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
tick.on('tick', ...
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('wakeup', ...
After:
modules.define('i-bem__dom', ['idle'], function(provide, idle, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
idle.on('wakeup', ...
The BEM blocks
If you have BEM blocks just containing some modules without using BEM methodology in them, you can now rewrite them as modules.
Before:
BEM.decl('i-router', {
route : function() { ... }
});
After:
modules.define('router', function(provide) {
provide({
route : function() { ... }
});
});
If you need BEM blocks (not BEM.DOM blocks) anyway, you can extend the i-bem
module.
Before:
BEM.decl('my-block', { ... });
After:
modules.define('i-bem', function(provide, BEM) {
BEM.decl('my-block', { ... });
provide(BEM);
});
The example of migration refactoring for the b-spin block
Before:
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')) { /* A dirty hack for IE which cannot get a background-position property but packground-position-y only */
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);
}
});
After:
modules.define(
'i-bem__dom',
['tick'],
function(provide, tick, BEMDOM) {
var FRAME_COUNT = 36;
BEMDOM.decl('b-spin', {
onSetMod : {
'js' : {
'inited' : function() { // constructor
var hasBackgroundPositionY = !!this.elem('icon').css('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() { // destructor
this._unbindFromTick();
}
},
'progress' : {
'yes' : 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);
});