【翻译】十大要避免的Ext JS开发方法

Advertisement

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

作者:Sean Lanktree
Sean is an Ext JS Professional Services Lead at CNX Corporation.

CNX,尽管大多数的Ext JS开发工作需要从0开始创建新的应用程序,偶尔会有客户让我们帮他们解决内部工作上的性能问题、臭虫和结构性问题。我们以“清洁工”这种角色进行工作已经有很长一段时间了,在我们审查过的应用程序中,我们注意到,有一些共同的不明智的编码方法经常会出现。基于过去几年的审查工作,我们列出了十个我们建议的,在Ext JS应用程序中应该避免的开发方法。

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

开发人员最常见的错误之一是没理由的嵌套组件。这样做,会影响性能和也会造成应用程序的不美观,如爽边框火意外的布局行为。在下面的示例1A,在面板内只包含了一个Grid。在这种情况下,该面板是不必要的。如示例1B所示,额外的面板可以取消。要记住的是,表单面板、树面板、标签面板和Grid面板都是从面板扩展的,隐藏,在使用这些组件的时候,应该特别注意不要的嵌套情况。

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

示例1A 不好的:面板(panel)是不必要的

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

示例1B 好:Grid已经是面板,因而可以直接在Grid中使用任何面板属性

2. 清理未使用组件失败造成内存泄漏

许多开发人员不知道为什么他们的应用程序随着使用时间越长越来越慢。在用户浏览整个应用程序期间清理未使用组件失败是最大的一个原因。在下面的实例2A中,每次用户右键单击Grid的行,都会创建一个新的右键菜单。如果用户保持应用程序处于打开状态并右键单击行上百次,那么,就会有上百个永远不会被摧毁的右键菜单。对于开发人员和用户来说,应用程序“看上去”显示是争取的是因为只有最后一个被创建的菜单能显示在页面上,而且与的则是隐藏的。由于没有创建新菜单并没有清理旧的,应用程序的内存利用率就会不断增长,这最终将导致较慢的操作或浏览器崩溃。

示例2A就很好,由于右键菜单只在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());

    }
});

示例2A 不好:每一次右键单击都会创建菜单,且永远不会被销毁

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

示例2B 较好:菜单会在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());
    }
});

示例2C 最好:Grid被销毁时,右键菜单也被销毁

3.怪物控制器

当看到应用程序拥有一个上千行代码的超级控制器的时候,我们不知道震惊了多少次。我们更倾向于根据应用程序功能拆分控制器。例如,订单处理应用程序可能会有划分条目、出货量、客户查找等控制器。这将使导航和维护代码更容易。

一些开发人员喜欢根据视图拆分控制器。例如,如果一个应用程序有一个Grid和表单,这将会使用一个控制器来管理Grid和使用一个控制器来管理表单。并没有一个“正确”的方式来划分控制器逻辑,只要是一致的就行。要记住,控制器可以与其他控制器进行通信。在示例3A,可以看到如何检索其他的控制器并调用其中的方法。

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

示例3A 获取另一个控制器的引用并调用它的方法。

作为替代,也可以触发任何控制器可以监听到的应用程序层事件。在示例3B和3C,可以看到如何在一个控制器触发一个应用程序层事件并在另外一个控制器监听它。

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

示例3B 触发一个应用程序层事件

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

示例3C 在另一个控制器监听应用程序层事件

注意: 自从Ext JS 4.2开始,使用多个控制器变得更容易了——他们可以触发其他控制器可以直接监听的事件

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

这虽然不影响性能和操作,但会让找跟进应用程序结构变得困难。随着应用程序的增长,如果源代码很有组织,那么寻找源代码以及增加特性或功能就会很容易。我们常常看到许多开发人员会将所有视图(即使是很大的应用程序)如示例4A一样放在一个目录。我们建议如示例4B所示通过逻辑功能来组织视图。

示例4A 不好:所有视图都处于同样层次

示例4B 好:视图根据逻辑功能进行组织

5.全局变量的使用

即使全局变量是不好的已经广为人知,但仍然会在我们审查过的一些应用程序中看到他们的身影。使用全局变量的应用程序可能会有名称冲突等重大问题,并且很难去调试。不使用全局变量,可以在类中定义“属性”,并引用这些属性的getter和setter。

例如,假设应用程序需要记录最后选择的客户。一般情况下会如示例5A那样在应用程序中定义一个变量,这很容易且值可以很便利的被应用程序的其他部分使用。

myLastCustomer = 123456;

示例5A 不好:创建全局变量来存储最后的客户编号

作为替代,好的做法是创建一个用来保存属性的类来代替全局变量。在当前情况下,可以创建一个名为Runtime.js来保存应用程序中要使用到的运行属性。示例5B显示了Runtime.js在源代码结构中的位置。

示例5B Runtime.js文件的位置

示例5C显示了Runtime.js文件的内容,而示例5D显示了如何在app.js中“请求(require)”它。在应用程序中,就可以如5E或5F那样在任何地方“设置(set)”或“获取(get)”属性。

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

示例5C 用来为应用程序保存全局属性的Runtime.js文件示例

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

示例5D 在app.js文件中请求Runtime类

MyApp.config.setMyLastCustomer(12345);

示例5E 设置最后客户的方式

MyApp.config.getMyLastCustomer();

示例5F 获取最后客户的方式

6. id的使用

我们不建议在组件上使用id,因为每一个id必须是唯一的。而这样很容易导致不小时使用了相同的id,从而引起重复的DOM id(名称冲突)。相反,应该让框架来处理id的生成。使用Ext JS的组件查询,没有任何理由要为Ext JS组件指定一个id。示例6A显示了一个应用程序中的两段代码,在这里创建了两个不同的保存按钮,而这两个保存按钮的id都为“savebutton”,从而导致了名称冲突。显而易见,在下面的代码很容易找出名称冲突,但在一个大型应用程序中,要确定名称冲突会很困难。

// 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'
}]

示例6A 不好:为组件分配了重复id将造成名称冲突

替代方法是,如果需要手动确定每一个组件的,可以如示例6B那样使用itemid代替id。这就可以解决命名冲突,并且仍然可以通过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’
}]

示例6B 好:使用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’);

示例6C 好:使用itemid引用组件

7.不可靠的组件引用

有时候会看到代码利用组件位置来引用组件。应该避免出现这样的情况,因为有任何条目被增加、移除或嵌入不同的组件,就会导致错误。示例7A显示了几种常见情况。

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

var myWindow = myToolbar.ownerCt;

示例7A 不好:避免基于组件位置来获取组件引用

替代方法是,如示例7B那样使用组件查询或者最佳的up或down方法来返回引用。使用这种技术,代码就很少会因以后的结构或组件位置变化而导致错误。

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

var myWindow = myToolbar.up(‘window’);

示例7B 好:使用组件查询来返回相关引用

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

Sencha在为组件、属性或xtype等等命名的时候,会遵循某些大写/小写标准。为了避免混淆,保持代码清洁,应对遵循相同的标准。示例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);
    }
});

示例8A 粗体显示的地方为不正确的大写/小写命名

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

示例8B 粗体显示的地方为正确的大写/小写命名

另外,如果触发任何自定义事件,事件的名称应对是小写的。当然,不遵循这些约定,一切都仍然会工作,但为什么要迷失在标准之外并编写不太干净的代码?

9. 将组件约束在父组件的布局

在示例9A,面板总是会有“region:center”属性,因此,当想重用它的时候就可能行不通,例如将它放到“west”区域。

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

示例9A 坏:“center”区域不应该放在这里

代替方法是,如示例8B那样,在创建组件的时候才指定布局配置。这样,就可以将组件重用到任何你希望的地方,切不受布局配置的约束。

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

示例9B 好:在创建组件的时候才知道区域

如示例9C所示,也可以为组件提供一个默认的区域,如果需要,可以重写它。

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

示例9C 也很好:指定默认区域,在需要的时候重写

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

示例10A 不好:为表单字段单独的从记录中加载数据

替代单独加载每一个值的方法是,使用loadRecord方法从记录中为所有字段的数据到表单字段,这只需要一行代码。如示例10B所示,这里的关键是确保表单字段的name属性和记录的字段名称是一样的。

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

myForm.loadRecord(record);

示例10B 好:使用loadRecord方法只需要一行代码就可加载所有表单字段

这只是比必需的更复杂的代码的其中一个例子。而当中的重点是要审视组件的所有方法和和示例,以确保正在使用简单和适当的技术。

CNX公司是Sencha认证选择合作伙伴。Sencha合作伙伴网络是Sencha专业服务器团队的宝贵扩展。

自从1996年以来,CNX一直处于自定义商业应用程序开发的先行者。在2008年,CNX在Ext JS上对它的基于浏览器的用户界面开发进行了标准化,在2010年,又添加了Sencha Touch作为移动开发的标准。我们已经在世界各地,为教育、金融、食品、法律、物流、制造、出版和零售等许多行业的客户创建了一流的Web应用程序。我们的开发团队以芝加哥市中心的公司办公室为基地,可以处理任何规模的项目。CNX可以独立工作或与您的团队一起,以快速、经济高效的方式去实现项目目标。请查阅我们的网站http://www.cnxcorp.com。

Similar Posts:

  • [Ext JS 6 By Example 翻译] 第1章 – 入门指南

    转载自:http://www.jeeboot.com/archives/1211.html 前言 本来我是打算自己写一个系列的 ExtJS 6 学习笔记的,因为 ExtJS 6 目前的中文学习资料还很少.google 搜索资料时找到了一本国外牛人写的关于 ExtJS 6 的电子书 [Ext JS 6 By Example].这份资料在 PACKT上卖 35.99 刀的,当然了万能的 google 还是帮我下载到了 PDF 文档.大概看了一下,讲的很详细,例子也比较简单,容易理解,现我准备利用工作

  • Ext JS 构建 Ajax 应用程序

    Ext JS 项目最初的目的是扩展 YUI Library 提供的功能.YUI Library 的一个关键方面是跨浏览器支持,这也可以在 Ext JS 中找到.这种支持使开发人员在构建 Web 应用程序时不需要考虑目标浏览器. Ext JS 提供出色的性能.这个框架是完全面向对象且可扩展的.因为 Ext JS 是用 JavaScript 编写的,所以只需下载并安装,就可以使用 Ext JS 的特性. 许可协议 在采用一个新框架之前,一定要了解框架基于哪种许可协议条款.Ext JS 提供几个许可协

  • 【翻译】如何创建Ext JS暗黑主题之一

    原文:How to Create a Dark Ext JS Theme– Part 1 概述 我是不是都要演示我的Spotifinder Ext JS应用程序.它是一个很酷的应用程序,可连接到LastFm和Spotify.创建它的目的,是未了在培训课程中演示Ext JS的概念.它还展示了Ext JS中伟大的主题功能. 今年,我在SenchaCon展示了高级主题功能,并收到了一些关于如何创建Spotifinder应用程序主题的咨询.因此,我觉得编写一个教程来说明如何创建这个相当酷,且很好看的暗黑

  • 《.NET最佳实践》与Ext JS/Touch的团队开发

    概述 有不少开发人员都问过我,Ext JS/Touch是否支持团队开发?对于这个问题,我可以毫不犹豫的回答:支持.原因是在Sencha官网博客中客户示例中,有不少项目都是基于团队模式开发的. 那为什么会出现这个问题?我觉得问题的关键在于不知道如何去进行模块独立调试或做最终的整合.对于这个问题,我觉得<.NET最佳实践>这本书(下 文中简称为实践一书)或许会给大家带来一点启示.虽然这本书是针对.NET而写的,但我觉得,这对于Ext JS/Touch,甚至于其他开发语言的开发,还是有不错的借鉴意义

  • 【翻译】在Ext JS集成第三方库

    原文地址:http://www.sencha.com/blog/integrating-ext-js-with-3rd-party-libraries/ 作者:Kevin Kazmierczak Kevin Kazmierczak is a Senior Technical Architect at Universal Mind, an innovative digital solutions agency that fuses the design capabilities of an int

  • 【翻译】Sencha Ext JS 5公布

    原文:Announcing Sencha Ext JS 5 简单介绍 我代表Sencha和整个Ext JS团队,非常自豪的宣布,在今天,Sencha Ext JS 5公布了.Ext JS 5已经迈出了一大步,在这里,我们还想花一点时间来感谢我们的社区为我们提供的全部反馈和不断的支持.在我们的历史上.这是最大的一次beta版本号修订,超过了10万的下载量.与你们一起,我们创建了世界上最先进的多设备JavaScript框架.有了你们的宝贵參与.才让我们建立了最好的Ext JS框架. 新东西 触屏支持

  • Ext JS 4.0.1更新说明(未翻译)

    Release Notes for Ext JS 4.0.1 Release Notes: May 17, 2011 Version Number: 4.0.1 Bugs Fixed Charts and Drawing [EXTJSIV-144] - Grouped Stacked Negative 0 point not aligned properly [EXTJSIV-148] - Pie chart labels should be along the centerline of th

  • 【翻译】Ext JS最新技巧

    原文:Top Support Tips Mitchell Simoens:控制滚动指示器的自动隐藏 Sencha Touch有一个跨平台的,在所有平台看起来和工作效果都一样的滚动条.两条轴(x和y,水平和垂直的区别)都有他们自己的滚动指示器,正如你所期望的,默认情况下,他们是隐藏的.不过,在某些情况下,可能需要一直显示指示器.在新的Touch 2.3.0种,每个指示器都有一个autoHide配置项来控制指示器的显示,设置autoHide为false将不再自动隐藏,在容器或它的子类内的scroll

  • 《Node.js开发加密货币》之二十二:自序

    亿书和数字出版新时代 恰在<Node.js开发加密货币>内容撰写过半,与出版社签订出版合同的时候(声明:电子书永远免费提供,内容同步),巴比特论坛和比特时代组织了一场以"我和时代的故事"为主题的征文活动.一语双关的活动主题,响亮而鲜明,我被狠狠地触动了一下.于是,打开电脑,写下这些回忆,权当给本书加上自序. 我和我的比特时代 我钟爱编程,近乎狂热,就像别人爱好足球一样,了解我的朋友都知道这一点.2009年,刚参加工作几年,一切还算顺利.有一天,接到一位留学美国的同学一封邮件

  • Ext JS vs. Google Closure

    阅读前请先注意,这是我翻译某君的ext与google closure的比较.翻译后,总体感觉此文个人色彩浓烈,明显是Ext Fans,无不借Closure来反衬Ext何其了得,--我觉得这样的态度是要不得的.为客观比较,这里提醒一下读者稍加注意. 自从Google释出了其Closure的JavaScript库以来,越来越多人希望了解它与Ext JS比起来究竟怎么样.由于我也属于这些想知道的人,所以我希望从我自己的角度来回答此问题,希望并不会由此触动双方的感情. Hello World 切入某个库

Tags: