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
objects
module to operate on objects (withextend
,isEmpty
andeach
methods) *thefunctions
module to operate on functions (withisFunction
andnoop
methods)
All the jQuery plugins which are not fo DOM operation became modules:
the
events
module used to be the$.observable
jQuery pluginIt works with events, provides "classes"EventsEmitter
andEvent
the
inherit
module used to be the$.inherit
pluginIt provides an inherit module with classes.the
cookie
module used to be the$.cookie
pluginthe
identify
module used to be$.identify
pluginthe
functions__throttle
andfunctions__debounce
used to be the$.throttle
and the$.debounce
plugins
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);
});