原文:Using Plugins and Mixins in Your Sencha Apps
当扩展一个框架类的功能的时候,通常都会直接将新功能写入派生类,然而,如果所需的同一功能存在于多个组件,那最有效的方式就是将它定义为一个插件或混入。插件和混入都是用来将额外功能添加到其他类的类。在本文,将介绍这些类是什么,他们之间的区别,以及他们如何工作。在Sencha Fiddle,我们准备了一些示例来演示这些概念。
插件是一个类,用来为 Ext.Component(或派生于 Ext.Component的类)添加或修改功能,与其他类一样,插件要使用Ext.define方法来定义,且扩展于Ext.plugin.Abstract。
// Simple example showing how to define a plugin // extending form Ext.plugin.Abstract Ext.define('Fiddle.plugin.SamplePlugin', { extend: 'Ext.plugin.Abstract', alias: 'plugin.sampleplugin', init: function (cmp) { this.setCmp(cmp); } });
方法init是插件的入口。它允许插件在组件渲染之前插入组件并与组件进行交互。在这个阶段,插件需要存储客户组件的引用,以便插件定义的方法可以很容易的引用它。Ext.plugin.Abstract 提供了两个访问器方法来引用使用该插件的组件。
方法getCmp尤为主要,因为插件与它的方法是在插件的作用域范围内工作,也就是说,this引用的是插件自身,而不是使用插件的组件。当插件需要与它拥有者的组件的进行交互的时候,访问方法(getter)就可在访问客户组件的时候派上用场。
方法init可以在init被处理的时候设置插件逻辑,可能还需要在它拥有者组件的HTML渲染后再次设置插件逻辑。例如,一个拖拽区域需要在组件的HMTL被渲染到DOM后才能被创建。在这种情形,就需要监听客户组件的afterrender事件并在这时候设置组件的功能。【示例】
插件的destroy方法会在组件销毁的时候被调用。通过插件创建的任何类实例(例如,拖与放、键盘导航等等)需要在这时候以编程的方式被销毁。任何被设置到客户组件实例的便利属性也必须被设置为null或删除。【示例】
方法enable只是用来将插件的disabled属性设置为false的,而disable方法会将disabled设置为true。在创建自己的插件时,可以扩展该功能。还可以在处理之前先检测disabled状态来决定是否使用任何插件逻辑。正如你所希望的,还可以更进一步去使用他们,具体取决于如何在需要时去启用或禁用插件。【示例】
在使用的时候,由于插件中定义的方法是属于插件而不是组件,因而,需要从组件获取一个引用并将它返回个插件来调用它的方法。又或者,将插件的方法绑定到组件会更方便。【示例】
在定义插件类的时候,给插件的别名这样一个前缀非常有用:alias:plugin.mypulugin。在客户组件使用插件的时候,就可以很容易的通过类型来设置插件:
plugins: [{ ptype: 'myplugin' }]
var thePlugin = owningClass.findPlugin(‘myplugin');
plugins: [{ ptype: 'myplugin', pluginId: 'myPluginId'; }] var thePlugin = owningClass.getPlugin(‘myPluginId');
混入也是用来为类添加功能的类。然而,它与插件的运作方式有以下的不同:
当创建了一个使用混入的类的实例的时候,就可以直接在类中调用任何混入定义的方法。这些方法的内的this作用域就是类自身。【示例】有可能在混入中定义的方法会与类自身的方法同名。在这种情况,混入方法就不会被复制到目标类的原型。这事,调用该类的方法将会一直是原始定义的方法。
要调用同名的混入方法,就要从所属类中获取到混入的引用,然后再直接调用混入发昂发。在直接调用混入的方法时,它的作用域将是混入类,因而,this指向的会是混入类自身【示例】。如下面示例,如果混入有一个混入id为util,调用混入定义的destroy方法将会是这样:
this.mixins.util.destroy.call(this);
尽管我们建议通过扩展Ext.Mixin来定义混入了,但混入也可以通过Ext.define来直接定义。通过扩展Ext.Mixin来定义的主要好处是在定义混入类的时候,允许定义“钩子”。钩子是定义在混入类的方法,会在接收类的相应的方法之前或之后自动被调用,例如,要确保混入的afterDestroy方法在类被销毁之后调用,可以使用after钩子:
mixinConfig: { after: { destroy: 'afterDestroy' } }
使用自己混入的首选方式是在数组中使用完整的类名。配置项mixins中的混入类会依照数组中的列出的顺序进行处理。
mixins: [ 'My.utility.MixinClass' // "util" is used to reference the mixin ]
可能需要从类实例中获取类的混入的引用。这可通过所属类的实例的mixins属性,再通过混入的id来引用(例如:this.mixins.util)。建议的做法是在定义混入类的时候总是为混入类设置一个唯一的混入id。有以下三种方式来设置或确定混入类的id:
mixinId: 'util'
mixinConfig: { id: 'util' }
mixins: { util: “My.utility.MixinClass” }
Ext.define('My.utility.MixinClass'); var utilMixin = owningClass.mixins['My.utility.MixinClass'];
现在,已经了解了插件和混入,那么,在定义类的时候,何时使用插件,何时使用混入呢?由于相同的功能可能无法写入无法写入任一类的类型,这时候就要考虑如何在应用程序内使用这些功能。插件更具灵活性,因为它可以在实例上使用,且只对该实例添加开销。然而,如果功能是针对所有类的,那么在混入内定义逻辑就更有效率,因为插件示例不会在每一个实例被创建的时候被创建。
作者:Seth Lemmons
Seth Lemmons is a Senior Engineer on the Sencha Support team. He has experience in Ext JS, Sencha Touch, and software development. He lives in Boise, Idaho with his wife and ~200k other very nice people. Look for slemmon on the forums.