10 大Extjs开发应该避免的错误

1.过多不必要的组件嵌套结构

最常见的错误之一是开发人员没有理由的嵌套组件。这样做不仅会影响程序性能,也能导致双边框或意想不到的布局行为。在下面的例子1A,我们有一个panel,其中包含一个grid。在这种情况下,该面板是不必要的。在例1B所示,额外panel可以被消除。forms, trees, tab panels和grids扩展自panel,因此在使用这些组件时,应该特别注意不必要的嵌套使用组件。

items: [{
    xtype : 'panel',
    title: ‘My Cool Grid’,
    layout: ‘fit’,
    items : [{
        xtype : 'grid',
        store : 'MyStore',
        columns : [{...}]
    }]
}]

Example 1A. BAD:这个panel不是必须的。

layout: ‘fit’,
items: [{
    xtype : 'grid',
    title: ‘My Cool Grid’,
    store : 'MyStore',
    columns : [{...}]
}]

Example 1B. GOOD:grid已经是一个panel了,因此可以直接用panel的属性在grid上。

2.内存泄漏导致清理未使用组件失败

许多开发人员惊讶不知道为什么他们的应用越来越慢,用的时间越长越慢。在用户使用应用过程中未能清理未使用的组件是应用程序失败的最大的原因。在下面的例子2A,每次用户右键点击grid行,创建一个新的右键菜单。如果用户在此应用程序右击grid行数百次,将会有数百个右键菜单没有销毁。对开发者和用户来说,应用程序看起来是正常的那是因为每次看到的是最后一次创建的右键菜单,其它的都被隐藏了。应用程序用的内存将会不断增加,最终导致较慢的操作或浏览器崩溃。例2B正确,因为右键菜单创建一次在grid初始化的时候,可以每次重用已经创建的右键菜单,然而当grid销毁时,右键菜单仍然存在虽然它不再需要了。最好情形是例子2C,右键菜单会被销毁当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());
 
    }
});

Example 2A. BAD:每次右击都会创建右键菜单但一直没有销毁。

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
        });
    },
 
    buildMenu : function(){
        return Ext.create('Ext.menu.Menu',{
            items : [{
                text : 'Do Something'
            }]
        });
    },
 
    onItemContextMenu : function(view,rec,item,index,event){
        event.stopEvent();
        this.menu.showAt(event.getXY());
    }
});
 


Example2B. BETTER: 菜单在grid创建时创建,每次右击可以重用已有菜单。

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
        });
    },
 
    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());
    }
});


Example 2C. BEST: 当grid销毁时,右键菜单也销毁了。

3.巨大的控制器

你经常惊讶的发现一个控制器有数千行代码。我们倾向于按功能细分我们的控制器。例如,一个订单处理应用有可能有项目,出货量,顾客查找等。在浏览维护代码时将会更容易。一些开发者喜欢划分控制器按view。例如如果应用有一个grid和form,有一个控制器管理grid,一个控制器管理form。没有一个“正确”一直方式划分控制逻辑。仅仅记住,控制器可以跟其它的控制器交互。在例子3A,你能知道怎么获得另一个控制器的引用调用控制器的方法。

 

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

Example3A.获得另外一个控制器的引用并调用方法。

或者你可以触发一个应用级的事件任何一个控制器都可以监听到。例子3B和3C,你能明白一个控制器可以触发一个应用级事件另一个控制器可以监听到。

MyApp.getApplication().fireEvent('myevent');


Example3B 触发一个应用级事件。

MyApp.getApplication().on({
    myevent : doSomething
});

Example 3C.另外一个控制器可以监听到应用级事件。

注:用Ext JS4.2开始,它变得更容易使用多个控制器 - 他们可以触发事件,其他控制器可以直接听。

4.源代码的文件夹结构差

这不会影响性能或操作,但你应用程序的结构,很难扩展。随着你的应用程序的发展,查看源代码和增加新的特性或功能将会非常容易组织你的源代码。许多开发者把多个views放到一个目录如例子4A。我们建议组织views按功能划分如例子4B。

Example4A. BAD:所有views在同一目录。

Example4B. GOOD: views组织按功能划分。

5.使用全局变量

 众所周知使用时不好的,但在很多应用我们仍然能够看见使用全局变量。在应用程序中使用全局变量最明显的问题就是有可能全局变量名称冲突,调试起来有很困难确定出问题代码位置。取代全局变量,我们把一些属性放到一个单例类,对属性操作时用getter和setter方法。例如,需要记住最后选中的客户。你有可能定义一个变量如例5A.非常容易在你程序有一样的变量名称的变量被定义。

myLastCustomer = 123456

Example5A. BAD:定义全局变量存储最后客户。
更好的方法是创建一个类,用类的属性存储原来全局变量存储的值。我们将创建名为Runtime.js文件存储运行时一些全局的属性。例子5B显示了Runtime.js整个代码结构的位置。

Example5B. Runtime.js文件位置

例5C显示了Runtime.js文件的内容,例5D显示怎么在app.js中动态加载定义的类。例5E和5F显示了在应用其它地方用get和set操作这些属性。

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

Example5C. Runtime.js文件样例代码显示了保存一个应用程序的全局属性。

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

Example5D.在app.js文件添加必须类MyApp.config.Runtime。

MyApp.config.setMyLastCustomer(12345);

Example5E. 怎么设置lastcustomer。

MyApp.config.getMyLastCustomer();

Example5F.怎么获得lastcustomer。

6.id的滥用

我们不建议使用组件的ID属性,因为每个ID都必须是唯一的。这太容易使用相同的ID不止一次,这将导致重复的DOM ID(名称冲突)。相反,让框架自动生成ID用Ext JS的ComponentQuery,就不会给Ext JS组件指定一个重复的id。例如6A显示一个应用程序有两个不同的保存按钮,这两者id一样都是‘SaveButton',虽然是两个代码段但两个按钮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'
}]


Example6A. BAD: 错误的组件id赋值导致id相同冲突。

相反,如果你想手动标识每个组件,你可以用'itemid'替代'id'如例如6B所示。解决了名称冲突,我们用itemid仍然可以得到一个组件的引用。有许多方法通过itemId获得一个组件引用。如例6C所示的几种方法。

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’
}]

Example6B. GOOD: 创建带属性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’);

Example6C. GOOD:引用组件通过itemId。

7.不可靠的引用组件

我们有时候看到用组件的位置去获得组件的引用。这是应该避免的,因为组件数组很容易添加,删除,导致组件位置发生变化。例7A一组常见情况。

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

Example7A. BAD: 避免通过组件位置获得组件的引用。
相反,使用ComponentQuery,或组件的“up”或“down”方法去获得组件引用,如例7B中所示。随后代码即使发生了变化,组件的结构和顺序变化后获取组件代码出错的可能性小。

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


Example7B. GOOD: 用ComponentQuery获得组件的引用。

8.不遵守大写/小写命名约定

在Sencha命名components, properties, xtypes等时需要遵守一些大小写规则。为了避免混淆,并让你的代码干净,你应该遵循同样的标准。如例8A显示了一些不正确情况。例8B显示了相同的情况下,用正确的大写/小写命名约定。

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);
    }
});

Example8A. BAD: 粗体代码用了错误的大小写命名。

Ext.define(‘MyApp.view.CustomerList’,{      
    extend : ‘Ext.grid.Panel’,
    alias : ‘widget.customerlist’,      
    myCustomConfig : ‘xyz’,            
    initComponent : function(){
        Ext.apply(this,{
            store : ‘Customers’,
            ….
        });
        this.callParent(arguments);
    }
});

Example8B. GOOD: 上面代码用了正确的大小写。

此外,如果你触发自定义事件,这个事件名称最好全部小写。当然,如果你不遵守这些约定你代码仍然可以正常运行,但为什么要写一些不标准,不干净的代码呢?

9.创建组件依赖父组件布局

如例9A,这个panel将会一直有属性‘region:center’,如果你想重用这个组件在“west”区域将会不能正常工作。

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

Example9A. BAD: ‘center’区域不应该这赋值。

相反,组件的布局信息应该在组件创建时赋值如例9B。通过这种方式,在你喜欢的任何地方你可以重用此组件,并不会受到布局配置的制约。

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' 
});
 

Example9B. GOOD: 设置区域在组件创建时。

如例9C所示,你也可以提供一个默认的“region”,,有必要可以重写的组件的“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
});

Example9C. Also GOOD:设置默认的“region”属性,有必要可以覆盖此属性。

10.代码比实际的需要更复杂

有很多时候,我们看到了比需要更复杂的代码。这通常是不完全熟悉每个组件的方法的结果。我们看到的最常见的情况之一是代码单独加载每个表单字段从数据记录。如例10A所示。

//  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’));
 

Example10A. BAD: 错误的手动加载表单属性从record。

用loadRecord方法加载所有的字段从record到表单的每个字段用一行,而不是手动加载每个值。关键是确保表单字段的“name”属性跟record的字段的name属性一致如10B所示。

items : [{
    fieldLabel : ‘User’,
    name : ‘UserName’
},{
    fieldLabel : ‘Email’,
    name : ‘Email’
},{
    fieldLabel : ‘Home Address’,
    name : ‘Address’
}];
 
myForm.loadRecord(record);
 


Example10B. GOOD: 用一行代码加载所有字段值。

这仅仅是个比需要更复杂的例子代码.关键是清楚每个组件的方法和例子以确保用了简单合适的技术。

文章其余部分是CNX公司信息如下

CNX Corporation is a Sencha Certified Select Partner. The Sencha Partner Network is a valuable extension of the Sencha Professional Services team.
CNX has been a leader in the development of custom business apps since 1996. CNX standardized its browser-based user interface development on Ext JS in 2008, adding Sencha Touch as a standard for mobile development in 2010. We have created world-class web apps for customers around the world in many industries including Education, Finance, Food, Law, Logistics, Manufacturing, Publishing and Retail. Our development teams are based at our corporate office in downtown Chicago and can handle projects of any scale. CNX can work independently or with your team to achieve project goals in a fast and cost-effective manner. Check us out at http://www.cnxcorp.com.
Written by Sean Lanktree
Sean is an Ext JS Professional Services Lead at CNX Corporation.

未作翻译。

原英文文章地址 http://www.sencha.com/blog/top-10-ext-js-development-practices-to-avoid/

转载请注明出处,谢谢。








 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值