翻译自《Tips and Tricks for Ext JS Component Developers》August 27, 2010 by Aaron Conran 转载请注明Ext中文网。
ExtJS 默认提供的是常见组件,但要想囊括全部需求,却几乎是不可能的。——故所以便有自定义组件的必要了。令人感到可喜的是,现今的 Sencha 社区就有一个高质量的 Ext组件社区,形成一个富足的生态系统。而在这里,我就有关怎么写组件时的提示和技巧,为大家介绍一番。
带缓冲的方法调用
当接收到大量的事件,如洪水一般猛烈的时候,ExtJS 允许你将这些事件的执行过程缓冲起来,怎么执行?就是在事件被触发之前,预先设定好的一个时间,等待这个时间过去才执行。这不但对于提升性能来说很有帮助的,同时也能降低网络流量。举例来说,我们有一个呈现主次关系的 Grid,根据记录的值更新详细信息的显示,此时我们通过一个 AJAX 请求来实现。可能出现以下的代码:
var sm = this.getSelectionModel();
sm.on('rowselect', this.onGridRowSelect, this, {buffer: 300});
在用户点击某行加上 300 毫秒过去后,Ext 才会执行onGridRowSelect() 方法。该事件处理手段在 Ext 中是相当典型的。如果你还不太熟悉可用的配置选项,那么你应该查看文档中的
addListener,其中有许多跟 buffer、single 类似的选项,十分有用。
Ext 使用了 Ext.util.DelayedTask 来达到那种缓冲的效果,那是一个底层的类。DelayedTask 可以把批量操作打包执行的一个很有用的类。例如,当在操控 DOM 的时候,避免同时执行多个步骤就使用使用缓时的策略去完成。也就是说,此时你应当把进行繁重处理工作放到另外一个函数中然后使用 DelayedTask,并指定一定的时间。
创建 DelayedTask 类时,指定你想执行的函数作为构造函数的第一个参数。一种比较常见的类写法如下:
//public api
something: function() {
if (!this.somethingTask) {
this.somethingTask = new Ext.util.DelayedTask(this.doSomething, this);
}
this.somethingTask.delay(this.somethingTaskDelay);
},
//private
doSomething: function() {
// heavy processing which needs
to be buffered
alert('something executed!');
}
这将会允许你的用户组件在一行中执行“something”数次,同时“doSomething”只会在“somethingTaskDelay”之后被执行。当与 DOM 交互时(比如 DOM reflows),这样可优化耗时且不必要的操作,十分简单。
允许用户在渲染组件前调用方法
编写组件时,你或者经常会有这样的需求,就是在渲染前,只执行一次就足够了,但渲染后却可以执行多次。要达到此目的,可以先检查“rendered”属性的标记来判定是否已经渲染了组件,然后设置一次性执行的事件处理器,以在渲染组件后重新调用该方法。以后就允许组件的用户在组件生命期的任意时刻调用这个方法,而不论是否渲染了组件。
someMethod: function() {
if (!this.rendered) {
this.on('render', this.someMethod, this, {single: true});
return;
}
// typical processing
}
另外常见的例子,则是用户希望调用那个方法多次,但是只在渲染过后仅发生一次。这种实现如下:someMethod: function() {
if (!this.rendered && !this.someMethodEventSetup){
this.someMethodEventSetup = true;
this.on('render', this.someMethod, this, {single: true});
return;
}
// typical processing
}
在一定的时刻提供事件
自定义组件应该在某些关键时刻提供相关事件支持,比如说组件展开或者收缩的时候。由于你的组件都是 Ext.util.Observable 的子类,你将会通过继承得到“addEvents”和“fireEvent”方法。“addEvents”的作用就是定义组件事件。“fireEvent”的作用就是通知抽象的订阅者一个事件已经发生,次数不限。如果有心研究一下的话,请看看 Ext JS 组件源代码来学习在何处添加事件。
事件应当:
- 提供在那时到底是什么发生了的信息
- 应提供有一个“beforeXXX”事件,在原事件之前发生,并可以在“beforeXXX”事件中替 XXX 事件作取消的决定,这样接下来的 XXX 事件就不会发生发生
为了在组件中定义事件,你可以在 initComponent() 初始化组件方法之中“添加”或者定义事件。每一个你触发的事件都应该通过 addEvents() 方法在定义时所规定的参数。
举例来说:
initComponent: function() {
MyCustomComponent.superclass.initComponent.call(this);
this.addEvents('beforeexpand', 'expand');
},
可以调用 fireEvent 方法触发事件。第一个参数是事件名称,其它的附加参数也会被传给事件处理器。
比如说:
this.fireEvent('expand', this, cmp, e);
假设在 Ext.Container 容器中,我们打算暴露一个展开 Expand 的事件,并随之执行容器自身的一个方法(实际上容器的展开,可以这么说是 DOM 的事件触发了 expand 事件的这么行为)。这个过程很简单,可以用 on()/addEventListener() 方法定义:
myCt.on('expand', function(ct, cmp, e) {
});
透过提供一个“预告事件”来允许用户取消某个行为是很重要的。在 Ext JS 中,如果在任何一个“预告事件”中返回 false,那么真正的目标事件将不会触发执行。因为 Ext JS 中的事件是同步的,要事件处理器不返回 false,实际才会真正去执行,如下例:
if (this.fireEvent('beforeexpand', this, cmp, e) !== false) {
// 执行展开
this.fireEvent('expand', this, cmp, e);
}
通过认识这种与 Ext JS 组件相似的模式,尽可能地反复表现出来,其他开发者会感到你写的组件让他们更加熟悉地上手,并可以更切合他们的应用,以便修改……
注释你的代码
一份良好质量的源码怎么能少了清晰可读的注释呢?没错,不要偷懒,都给你的公共的方法、公共属性、配置项的等写上注释。哪怕是自己弄的程序,也要最好养成写注释的习惯。无论是自己还是别人,看文本肯定要比看计算机代码舒服,尤其是你在隔几个天后回头看代码的时候。Ext 是采用类似 JSDoc,(但有不一致的些地方,译者注)规范的注释。你可以使用
Ext Doc这个项目来生成你自己写好的注释。
结语
算一算,自本人写博客以来已经有一段时间了,期间看到许多开发者写组件有这样或那样的问题或不足。鉴于此,我希望通过不断强调这些常见的模式,能够不但是能够给予开发者们新的灵感,而且是对提高组件质量实际可行的方式方法。还有建议大家的是,不妨多看看咱们的用户扩展专区。当然我们也鼓励大家提交你的作品上来,这样经过多人使用,便有多番的测试和感受。突破原来的某些领域上的限制,这便是社区赋予组件开发的一种艺术!
眼见及下,不少开发者都已经是有经验的开发者,由此由衷希望你们分享你们所得的经验或者技巧,还有意见,这都是对整个社区非常有益的。如果你只是想扩展一下 Ext JS 组件,没有关系,其实这是说明 Ext JS 缺少了那些组件——这也是我们需要聆听社区的重要声音!