您的位置:首页 > Web前端 > JavaScript

10个应该避免的ExtJS开发实践

2016-03-02 14:28 686 查看
1、过度或不必要的组件嵌套

开发人员最常犯的错误之一就是没有理由的组件嵌套。这会损耗性能并且导致不够美观,比如双边框或布局异常等。在下面的例子中,有一个仅仅包含Grid的Panel,在这种情况下,Panel是不必要的。记住像Form、Tree、TabPanel和Grid都继承自Panel,因此在使用这些组件时,应特别注意不必要的组件嵌套情况。

items: [{
xtype : 'panel',
title: ‘My Cool Grid’,
layout: ‘fit’,
items : [{
xtype : 'grid',
store : 'MyStore',
columns : [{...}]
}]
}]
BAD:这里的Panel是不必要的。

layout: ‘fit’,
items: [{
xtype : 'grid',
title: ‘My Cool Grid’,
store : 'MyStore',
columns : [{...}]
}]
Good:Grid是Panel的子类,因此可以直接在Grid上使用Panel的属性。

2、未清除没有使用的组件导致内存泄露

许多开发者都想知道,为什么应用使用的时间越长,响应速度就越慢。在应用中未清除没有使用的的组件是最大的原因。在下面的例子中,每次用户在Grid的行上点击右键菜单,一个新的Menu实例便会被创建。如果用户一直使用应用并右键点击了Grid行几百次,那便会有几百个Menu实例存在,并且永远不会被销毁。对开发者和用户来说,应用表面上看起来是正确的,因为只有最后创建的Menu实例才会显示在页面上,其余的全都被隐藏了。随着新的Menu实例被创建而老的又没有被销毁,应用消耗的内存会持续保持增长。这最终会导致操作变得越来越慢甚至浏览器崩溃。

在初始化Grid的时候,只创建Menu实例一次并在每次用户右键点击时进行复用,是比较好的一种改善方式。即便如此,当Grid被销毁时,上下文菜单虽然已经不需要了,但却依然存在。最好的方式是在Grid被销毁时,上下文菜单也随之进行销毁。

Ext.define('MyApp.view.MyGrid',{
extend : 'Ext.grid.Panel',
columns : [{...}],
store: ‘MyStore’,
initComponent : function(){
this.callParent(arguments);
this.on({
scope : this,
itemcontextmenu : this.onItemContextMenu
});
},

onItemContextMenu : function(view,rec,item,index,event){
event.stopEvent();
Ext.create('Ext.menu.Menu',{
items : [{
text : 'Do Something'
}]
}).showAt(event.getXY());

}
});
BAD:在每次右键点击时都会创建一个Menu实例,并且永远不会被销毁。

Ext.define('MyApp.view.MyGrid',{
extend : 'Ext.grid.Panel',
store : 'MyStore',
columns : [{...}],
initComponent : function(){
this.menu = this.buildMenu();
this.callParent(arguments);
this.on({
scope : this,
itemcontextmenu : this.onItemContextMenu,
destroy: this.onDestroy
});
},

buildMenu : function(){
return Ext.create('Ext.menu.Menu',{
items : [{
text : 'Do Something'
}]
});
},

onDestroy : function(){
this.menu.destroy();
this.callParent(arguments);
},

onItemContextMenu : function(view,rec,item,index,event){
event.stopEvent();
this.menu.showAt(event.getXY());
}
});


Better:当Grid被销毁的时候,上下文菜单也随之进行销毁。

3、巨大的控制器

这是令人惊奇的,我们看到的应用常常会有一个由数千行代码组成的巨大控制器。我们倾向于根据应用程序的功能来拆分我们的控制器,它使导航和维护代码更容易。很多开发者喜欢通过视图来拆分控制器。例如,如果一个应用有一个Grid和一个Form,那么将分别有一个管理Grid和管理From的控制器。只要你能始终保持一致,就没有一种绝对正确的方式来分离控制器逻辑。另外,控制器可以和另外控制器产生联系。下面的代码将演示如何检索其他的控制器并调用其方法。

this.getController('SomeOtherController').runSomeFunction(myParm);


你可以触发一个应用级别的事件,并且任何控制器都可以进行监听。下面的代码将演示如何在一个控制器中触发应用级别的事件,并在另一个控制器中对该事件进行监听。

MyApp.getApplication().fireEvent('myevent');
触发应用级别事件

MyApp.getApplication().on({
myevent : doSomething
});
在另一个控制器中进行监听

4、源码的文件组织较差

这并不会影响应用性能和操作,但却让人很难理清应用的文件组织结构。随着应用规模的扩大,如果你能很好地组织你的源码,那么在代码中迅速定位并添加新的特性和方法将变得比较容易。就像下图所示,很多开发者会把所有的视图文件(甚至整个项目)都放在同一个文件夹下。



我们推荐根据业务逻辑对视图文件进行组织分类:



5、使用全局变量

虽然使用全局变量的坏处众所周知,但我们还是在最近审查的很多项目中看到。使用全局变量的应用存在命名冲突、难以调试等重大问题。相比使用全局变量,更好的做法是使用类的“properies”,并通过getters和setters来引用这些属性。例如,为了让你的应用记住最后被选择的客户,你可能会像下面的代码一样临时定义一个变量,这很简单并且它的值在应用的所有部分都是有效的。

myLastCustomer = 123456;
BAD:使用全局变量来存储客户编号。

更好的实现方式是创建一个类,它持有的属性可以在全局范围内使用。在这种情况下,我们可以创建一个名为Runtime.js的文件来保存运行时属性。我们可以把该文件放在config文件夹下:



Runtime.js文件内容:

Ext.define(‘MyApp.config.Runtime’,{
singleton : true,
config : {
myLastCustomer : 0   // initialize to 0
},
constructor : function(config){
this.initConfig(config);
}
});
在应用中引用Runtime.js

Ext.application({
name : ‘MyApp’,
requires : [‘MyApp.config.Runtime’],
...
});

Set属性的值

MyApp.config.setMyLastCustomer(12345);
Get属性的值

MyApp.config.getMyLastCustomer();


6、使用"id"

因为每一个id都必须是唯一的,所以我们不推荐在组件上使用id。在项目中很容易多次使用相同的id,这将导致重复id的DOM(命名冲突)。相反,应该让框架来为你处理生成id的问题。有了ComponentQuery,就无需为组件定义一个id。下面的代码演示了在一个应用中存在两个不同的保存按钮,它们却有相同的id,这将导致命名冲突。虽然在下面的代码中很容易发现问题,但在一个庞大的实际项目中却很难。

// here we define the first save button
xtype : 'toolbar',
items : [{
text : ‘Save Picture’,
id : 'savebutton'
}]

// somewhere else in the code we have another component with an id of ‘savebutton’
xtype : 'toolbar',
items : [{
text : ‘Save Order’,
id : 'savebutton'
}]

BAD:给不同组件定义重复的id将导致命名冲突。

如果你想手动识别每个不同的组件,可以像下面的例子中使用itemId来代替id。这将解决命名冲突的问题,并且仍然可以通过itemId来获得对组件的引用。

xtype : 'toolbar',
itemId : ‘picturetoolbar’,
items : [{
text : 'Save Picture',
itemId : 'savebutton'
}]

// somewhere else in the code we have another component with an itemId of ‘savebutton’
xtype : 'toolbar',
itemId: ‘ordertoolbar’,
items : [{
text : ‘Save Order’,
itemId: ‘savebutton’
}]
有很多种方式可以通过itemId来获得对组件的引用,如下面的代码所示:

var pictureSaveButton = Ext.ComponentQuery.query('#picturetoolbar > #savebutton')[0];

var orderSaveButton = Ext.ComponentQuery.query('#ordertoolbar > #savebutton')[0];

// assuming we have a reference to the “picturetoolbar” as picToolbar
picToolbar.down(‘#savebutton’);


7、对组件不可靠的引用

我们时常看到一些代码,通过组件定位来获得对它的引用。如果有任何元素被添加、删除或嵌套在不同的组件里,这样的代码将很容易被破坏。下面的代码展示了常见的两种情况:

var mySaveButton = myToolbar.items.getAt(2);

var myWindow = myToolbar.ownerCt;
BAD:避免通过组件定位来获得对它的引用。

相反,应该像下面的代码中通过ComponentQuery或组件的up/down方法来获得对组件的引用。通过这种方式,代码将不太容易被破坏,即使组件的结构或顺序发生了改变。

var mySaveButton = myToolbar.down(‘#savebutton’);    // searching against itemId

var myWindow = myToolbar.up(‘window’);
GOOD:通过ComponentQuery来获得对组件的引用。

8、没有遵从有关大小写的命名约定

Ext在命名class、component、properties、xtype等时,遵从特定的大小写规范。为了避免混淆和代码清晰,应该始终遵从一致的命名规范。

Ext.define(‘MyApp.view.customerlist’,{          // should be capitalized and then camelCase
extend : ‘Ext.grid.Panel’,
alias : ‘widget.Customerlist’,                       // should be lowercase
MyCustomConfig : ‘xyz’,                            // should be camelCase
initComponent : function(){
Ext.apply(this,{
store : ‘Customers’,
….
});
this.callParent(arguments);
}
});
BAD:与Ext不一致的命名约定。

Ext.define(‘MyApp.view.CustomerList’,{
extend : ‘Ext.grid.Panel’,
alias : ‘widget.customerlist’,
myCustomConfig : ‘xyz’,
initComponent : function(){
Ext.apply(this,{
store : ‘Customers’,
….
});
this.callParent(arguments);
}
});
GOOD:保持与Ext一致的命名规范。

此外,如果你想要触发自定义事件,那事件的名称应该全小写。当然,即使你不遵从这些规范,原有的功能仍然可以正常运行,但是为什么要脱离标准并去写那些不够清晰的代码吗?

9、限定组件在其父组件中的布局

在下面的代码示例中,面板将总是拥有"region:center"属性,因此你将不能在其他布局方位中复用它。

Ext.define('MyApp.view.MyGrid',{
extend : 'Ext.grid.Panel',
initComponent : function(){
Ext.apply(this,{
store : ‘MyStore’,
region : 'center',
......
});
this.callParent(arguments);
}
});
BAD:region属性不应该在这个地方定义。

下面的例子则展示在实例化组件并进行显示的时候才定义布局属性,通过这种方式你可以在任何地方复用该组件并且不会被布局属性限制。

Ext.define('MyApp.view.MyGrid',{
extend : 'Ext.grid.Panel',
initComponent : function(){
Ext.apply(this,{
store : ‘MyStore’,
......
});
}
});

// specify the region when the component is created...
Ext.create('MyApp.view.MyGrid',{
region : 'center'
});
GOOD:在创建组件实例的时候定义布局属性region。

当然,你也可以给组件提供一个默认的布局属性,然后在必要的时候进行重写。

Ext.define('MyApp.view.MyGrid',{
extend : 'Ext.grid.Panel',
region : 'center', // default region
initComponent : function(){
Ext.apply(this,{
store : ‘MyStore’,
......
});
}
});

Ext.create(‘MyApp.view.MyGrid’,{
region : ‘north’, // overridden region
height : 400
});
GOOD:定义默认的布局属性,然后在需要的时候进行重写。

10、复杂化自己的代码

很多次我们看到的代码都比实际需要的要复杂,这通常是不完全熟悉每个组件的可用方法的结果。最常见到的一种情况就是从数据记录中分别去加载每一个表单字段。

//  suppose the following fields exist within a form
items : [{
fieldLabel : ‘User’,
itemId : ‘username’
},{
fieldLabel : ‘Email’,
itemId : ‘email’
},{
fieldLabel : ‘Home Address’,
itemId : ‘address’
}];

// you could load the values from a record into each form field individually
myForm.down(‘#username’).setValue(record.get(‘UserName’));
myForm.down(‘#email’).setValue(record.get(‘Email’));
myForm.down(‘#address’).setValue(record.get(‘Address’));
BAD:从数据记录中分别加载每一个表单字段。

其实,可以使用loadRecord方法从数据记录中一次性加载所有的表单字段,关键是表单字段的name属性和数据记录字段的name要保持一致。

items : [{
fieldLabel : ‘User’,
name : ‘UserName’
},{
fieldLabel : ‘Email’,
name : ‘Email’
},{
fieldLabel : ‘Home Address’,
name : ‘Address’
}];

myForm.loadRecord(record);
GOOD:使用loadRecord方法在一行代码中加载所有的表单字段。

以上只是众多代码复杂化的一个例子,解决这个问题的要点是要浏览所有的组件方法和实例,已确保自己正在使用简单、合适的技术。

原文链接:https://www.sencha.com/blog/top-10-ext-js-development-practices-to-avoid-2/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: