Base组件
Base被设计为那些衍生自Attribute和EventTarget的类的底层基础类。
为创建基于Attribute的对象提供标准模板,也为类的层次提供了一致的init()和destory()方法序列来串联初始化(initializer)和销毁(destructor)方法。
Base同时也通过plugins或extensions机制为类提供了代码重用的方法。
对应模块‘base‘, Base uses Attribute,Plugin.Host
使用Base的功能
1,扩展Base
Base作为实现了Attribute及EventTarget功能的基类,具有了Attribute配置及相关的事件注册功能,通过extend可以创建Base的子类
YUI().use("base", function(Y) { function MyClass(config) { MyClass.superclass.constructor.apply(this, arguments); } Y.extend(MyClass, Y.Base, { // Prototype methods for your new class }); });
Base的构造器函数支持接收一个字面量对象,可以在构造过程中用来设置属性的初始值。此外还接受事件和插件相关的参数,具体参考Base API
2,静态属性
Base需要两个静态的属性:NAME, ATTRS 。当设置事件或属性的时候会用到这两个属性。
示例如下:
function MyClass(config) { MyClass.superclass.constructor.apply(this, arguments); } MyClass.NAME = "myClass"; MyClass.ATTRS = { A : { // Attribute "A" configuration }, B : { // Attribute "B" configuration } } Y.extend(MyClass, Y.Base, { // Prototype methods for your new class });
NAME属性
用来标识类,它的用途是作为该类对象发布事件的前缀,如上例MyClass类实例发布事件要采用’myClass:'前缀。默认的约定的采用类名的驼峰拼写。
事件前缀保证了类事件的可识别性。为不同了类的同名事件提供了一种隔离机制。实际上在定义NAME属性前提下,如果在触发时不写前缀也同样可以触发事件。
为了保证代码的清晰,最好为按约定定义NAME并为自定义的类事件标记前缀!
下面的两个事件注册都会触发。
function MyClass(config){
MyClass.superclass.constructor.apply(this,arguments);
}
MyClass.NAME = 'myClass';
MyClass.ATTRS = {
a:{
value:'aa'
}
};
Y.extend(MyClass,Y.Base,{
say:function(){
this.fire('myClass:enabled');
}
});
var o = new MyClass();
o.on('enabled',function(e){
alert("enabled")
});
o.on('myClass:enabled',function(e){
alert('myClass:enabled')
})
o.say();
NAME属性还会在Base的toString默认实现中使用。
ATTRS属性
ATTRS属性是一个关联数组,为继承自Base的类提供默认的属性定义并添加到类的实例中。类实例将包含类层次结构中定义的所有属性。
属性由属性名和属性值配置组成,如下是Drag类的部分属性配置
Drag.ATTRS = { node: { setter: function(node) { var n = Y.one(node); if (!n) { Y.fail('DD.Drag: Invalid Node Given: ' + node); } return n; } }, dragNode: { setter: function(node) { var n = Y.one(node); if (!n) { Y.fail('DD.Drag: Invalid dragNode Given: ' + node); } return n; } }, offsetNode: { value: true }, clickPixelThresh: { value: DDM.get('clickPixelThresh') }, ... };
更多关于属性的配置可参考 属性配置参数列表
当初始化一个衍生自Base类的子类时,Base的init()方法会初始化类层级中每一个类的ATTRS定义的属性信息。这避免了每个类在constructor/initializer中重复定义属性初始化代码。
此外,Base定义了属性的初始化顺序:Base->子类。在ATTRS属性初始化过程中,如果一个属性用到后面定义的属性,后面的属性会按照需要进行初始化。
值得注意的是,Base考虑到性能的因素,默认采用懒加载方式初始化属性。也就是说知道调用该属性的时候才会被初始化。在Attribute介绍属性配置的时候提过lazyAdd配置属性,可以通过设置lazyAdd为false修改默认的惰性行为。
3,初始化(initialization)和销毁(destruction)
Base实现了final类型的init()和destory()两个方法来建立生命周期中初始化和销毁两个阶段。
Base的子类可以通过定义原型链上的initializer()和destructor()方法实现初始化和销毁的生命周期管理,Base通过init()和destory()方法遍历原型链来调用。
每个Base子类不需要调用父类的方法实现,Base会根据类层次按固定的顺序调用。
Y.extend(MyClass, Y.Base, { // Prototype methods for your new class // 该方法会在init()初始化过程中被调用 initializer : function(cfg) { this._wrapper = Y.Node.create('<div class="yui-wrapper"></div>'); }, // 该方法会在destory()销毁过程中被调用 destructor : function() { Y.Event.purgeElement(this._wrapper); this._wrapper.get("parentNode").removeChild(this._wrapper); this._wrapper = null; } });
initializer()
init()方法由Base构造器调用后,会按照类层次(Base first,subClass last)顺序调用每个类的initializer()方法。
在调用initializer方法前,会首先初始化类的属性(ATTRS及构造传入的属性),构造器传入的属性配置会通过init方法传递给initializer方法。
init方法触发init事件,可以在init事件回调函数中终止初始化过程。
destructor()
destory()方法调用后,会按照类层次(Base last,subClass first,顺序与初始化相反)调用每个类的destructor()方法。
如果子类在初始化和销毁过程中不需要执行任何代码,那么你就不需要定义initializer和destructor方法。
YUI3官方给出了定义Base子类的模板文件:http://developer.yahoo.com/yui/3/base/assets/mycomponent.js.txt
4,插件Plugins
Plugins功能可以为继承自Base的类实例对象自动的添加功能或新的特征,而不会影响核心对象的代码。
这一功能允许类实例混合多种功能而不需要将这些写到一个庞大的类中或由一系列衍生子类提供。
Plugin.Host为Base类提供了如下的方法:
plug(pluginClass,pluginConfig)
为实例添加插件及插件配置。plug方法实例化一个插件实例并添加到当前实例的属性上,具体的属性名在插件类的NS属性中定义。
plug方法允许通过数组的方式一次添加多个插件。
unplug(pluginClass) or unplug(namespace)
为实例对象移除插件并销毁。同时也会移除对象为插件增加的命名空间。这两个方法在插件实例创建后才能使用。
为实例添加插件,还可以通过plugins属性。
var overlay = new Y.Overlay({ srcNode: "#module", plugins : [{fn:AnimPlugin, cfg:{duration:2}}] });
此外,如果组件开发者希望在组件类定义时默认增加一些插件功能,可以使用Base.plug和Base.unplug方法。
关于Plugin的开发,可参考Plugin介绍, The Widget IO Plugin, Overlay IO Plugin and Overlay Animation Plugin 都是学习插件开发的好例子。
YUI官方给出了定义插件的模板文件:http://developer.yahoo.com/yui/3/plugin/assets/myplugin.js.txt
5,扩展Extensions
Base提供了一个静态的build方法,通过将一个主类和一个或多个扩展类组合来创建自定义类。与插件相似,它们把特定功能的实现封装在扩展类中。
不同的是,扩展是在类级别扩展类,创建新类;而插件则是在实例级别扩展实例。
build()方法可以将已经存在的主类与几个扩展类组合(mix)来创建新类。为新类增加来自扩展类的方法、属性和事件等。默认情况下,
build方法不会影响主类,并未为主类增加额外的功能;但是可以在需要的情况下通过配置选项是否影响主类。
/* Main Class */ function Panel(cfg) { Panel.superclass.constructor.apply(this, arguments); } Panel.ATTRS = { // Panel attributes close : { ... }, minimize : { ... }, shadow : { ... }, ... }; Y.extend(Panel, Y.Base, { // Panel methods show : function() { ... }, hide : function() { ... }, minimize : function() { ... } }; /* Additional Resizable Feature */ function Resizable() { this._initResizable(); } Resizable.ATTRS = { handles : { ... }, constrain : { ... } }; Resizable.prototype = { _initResizable : function() { ... } lock : function() { ... } }; /* Additional Modality Feature */ function Modal() { this._initModality(); } Modal.ATTRS = { modal : { ... }, region : { ... } }; Modal.prototype = { _initModality : function() { ... }, showMask() : function() { ... }, hideMask() : function() { ... } }; //创建新类WindowPanel,继承自Panel,并扩展Resize和Modal类的方法和属性。 var WindowPanel = Y.Base.build("windowPanel", Panel, [Resizable, Modal]); var wp = new WindowPanel({ shadow: true, modal: true, handles:["e", "s", "se"] }); wp.show(); wp.lock();
YUI官方给出了实现扩展的模板文件:http://developer.yahoo.com/yui/3/base/assets/myextension.js.txt
build方法内部实现:
1,继承主类创建新的类;
2,将所有扩展类的prototype方法扩展(augment)到主类中。
3,为新类合并(aggregate)所有已知的静态属性。 除了ATTRS里的属性,需要合并的其他静态属性标记在类的_buildCfg.aggregates定义中。
例如:除了Base定义了ATTRS属性,Widget的_buildCfg.aggregates中添加的HTML_PARSER属性也会被合并到新类。
当新类实例化时,在init阶段,新类的构造器将调用主类的构造器。
除了build静态方法,Base还提供了的create和mix静态方法实现扩展。
Base.create()
该方法可以传入额外的原型方法和静态属性。
Base.mix()
该方法可以在现有类上直接添加扩展类的方法。也就是上面提到的改变主类的实现方法。
以下是部分源码,从中可以了解三种方法的不同作用。具体说明请参看Base API。
Base.build = function(name, main, extensions, cfg) { return build(name, main, extensions, null, null, cfg); }; Base.create = function(name, base, extensions, px, sx) { return build(name, base, extensions, px, sx); }; Base.mix = function(main, extensions) { return build(null, main, extensions, null, null, {dynamic:false}); };