Ext JS——高效的编码风格指南

原文:ExtJS - Efficient coding style guide

作者:Raja

  1. 切勿使用“new”关键字:在Ext JS中,使用“new”关键字来创建一个组件或类的实例是一种错误的做法,因为这没有遵循组件的生命周期。应该使用Ext.create方法来创建对象,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. 错误: var obj = new Ext.panel.Panel();  
    2.   
    3. 正确: var obj = Ext.create(‘Ext.panel.Panel’);  
  2. 初始化直接量:不要直接创建直接量的对象,例如,使用以下javascript来创建空白对象:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  var stringObj = new String();  
    2. var arrayObj = new Array();  
    3. var obj = new Object();  

    在javascript中,以上都是创建对象的不正确方式,这是因为如果使用这些处理方式,控制需要去遍历整个类的层次。因此,作为替代,可以使用以下方式来创建这些类的对象:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  var stringObj = ‘’;  
    2. var arrayObj = [];  
    3. var obj = {};  

    不过,你接手的可能是别人编写的遗留代码,因而会注意到构造函数的一个“特色”(这或者是另一个不使用它的理由)。“特色”的主要问题是Object的构造函数可接受参数,并且它会根据传入的值来决定是否将对象的创建委托给另一个内置构造函数,而最终返回的对象可能不是你所预期的,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  // Warning: antipatterns ahead  
    2. // an empty object  
    3. var o = new Object();  
    4. console.log(o.constructor === Object); // true  
    5. // a number object  
    6. var o = new Object(1);  
    7. console.log(o.constructor === Number); // true  

  3. 更聪明的使用getCmp:它将根据传递的id值返回匹配的对象(Ext JS对象)。这是一种快速返回对象的方法。所有对象在创建的时候,都需要使用他们的id作为关键字注册为一个单一对象,这样,使用Ext.getCmp(myId)就可以寻找并返回RegistrationObject["myId"],因此,这会变得非常快捷。

    不过,如果都个组件使用了相同的id,它就会失效。在这种情况下,它将会返回最后一个查找到的对象。基于这点,建议尽量不要使用这个来获取对象。建议的做法是只使用该方法一次,然后将结果保存到一个变量,再在其他地方通过变量来引用对象。

    如果需要为多个组件定义相同的id,建议使用itemId。通过这篇文章可了解id和itemId直接的不同。

  4. 避免不比亚的全局变量:使用全局变量的主要问题就是它可在javascript应用程序或Web页面中共享所有代码。这可能会与使用相同名称的命名空间引起冲突,在应用程序的两个单独部分定义具有相同名称但用途不同的全局变量的可能性是存在的。使用不必要的全局变量的第二个缺点是会造成更大的内存损耗,这是因为,对于这些全局变量,一般情况下是不会进行垃圾回收的,因而不会释放内存。

  5. 使用var关键字来定义全局变量:使用var来定义全局变量与不使用之间的一个细微差别是:能不能使用delete操作符来删除这些变量。
    使用var来定义的全局变量(包括在函数内创建的)不能使用delete来删除,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.                  // define three globals  
    2. var global_var = 1;  
    3. global_novar = 2; // antipattern  
    4. (function () {  
    5.                 global_fromfunc = 3; // antipattern  
    6. }());  
    7. // attempt to delete  
    8. delete global_var; // false  
    9. delete global_novar; // true  
    10. delete global_fromfunc; // true  
    11. // test the deletion  
    12. typeof global_var; // "number"  
    13. typeof global_novar; // "undefined"  
    14. typeof global_fromfunc; // "undefined"  

  6. 尝试去删除那些未使用的变量和函数:不要保留代码中那些没有使用到的变量、函数或者不必要的注释,因为这些只会增加文件的大小,从而增加文件的加载时间。

  7. 避免在循环中创建对象或变量:如果没有必要,就不要在循环中创建单一的变量或对象,因为他们的数量会随着选好的迭代次数而增加,进而造成内存泄漏。

  8. 避免过度使用面板:在大多数情况下,Ext JS应用程序会受到面板过度使用的影响。无论什么情况都使用面板,或者更准确的说,是无论任何情况下都会使用多个面板。解决办法就是在使用一些轻量级的替代品来代替面板。很多人都喜欢面板,不过说实在,在UI的许多部分有点过分了。我曾经看到一个用来显示几十个缩略图的UI,每个图像都是使用不同面板的HTML内容来显示的,而这些面板是完全没必要的。可以考虑以下替代方法……
    • 是否可使用HTML和CSS,而不是完整的组件?使用Ext.Template或Ext.XTemplate可能更实际。
    • 是否可以使用自定义的Ext.Component?可以使用不同的配置项来将HTML猪肉到组件:autoEl、html、tpl或数据。
    • 是否可以使用数据视图(DataView)来渲染所有数据而不是单独的组件?
    • 是否可以使用Ext.container.Container,而不是Ext.panel.Panel来减少开销?

  9. 避免容器嵌套:尽量避免不必要地使用容器,因为每个容器都会创建一个层次结构,并以此为核心来访问子组件,而这,需要遍历这些容器的层次结构。因此,要尽量在最小的可能容器数量下创建视图。

  10. 函数要尽可能小:每一个函数都应短小精干和有意义。大的函数会降低可读性、可维护性、可重用性和可调试性。除此之外,如果一个对象(非常消耗内存的对象)在函数开始时进行实例化,它就会一直存在到函数结束。所以,如果函数非常小,那么,它的局部变量、对象等垃圾就可以在更短的时间跨度内被回收,从而释放内存于其他用途。

  11. 尽量避免过长的对象引用:应尽量避免大型对象的引用,因为这需要花费更多的时间来遍历对象的层次结构来获取所需的组件,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.   var arrayLength = arrayObj.lenght;  
    2.  forvar I = 0; I < arrayLenght; i++){  
    3.              //anti pattern  
    4.              If(Abc.xyz.foo.bar.againFoo.againBar.finalObj === arrayObj[i]){  
    5.                              Alert(‘Anti pattern code’);  
    6.              }  
    7. }  

    以上代码可重写为以下更有效的方式:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  var arrayLength = arrayObj.lenght;  
    2. var obj = Abc.xyz.foo.bar.againFoo.againBar.finalObj;  
    3. forvar I = 0; I < arrayLenght; i++){  
    4.             //pattern  
    5.             If(obj === arrayObj[i]){  
    6.                             Alert(‘pattern code’);  
    7.             }  
    8. }  

  12. 应避免吊装(hoisting)问题:javascript允许在一个函数内任何个地方使用多个var语句,而他们的行为与在函数顶部定义变量是没有区别的,这种行为通常被称为吊装。在使用变量之后再在函数内进行声明,可能会导致逻辑错误。对于javascript来说,只要变量在同一作用域(同一函数)被使用,它就会被声明,无论是否使用了var来定义,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. // antipattern  
    2. myname = "global"// global variable  
    3. function func() {  
    4.        alert(myname); // "undefined"  
    5.        var myname = "local";  
    6.        alert(myname); // "local"  
    7. }  
    8. func();  

    第一个alert将会显示“underfined”,这是因为myname被认为是在函数内部的局部变量(尽管是在之后声明的)。所以变量的定义都会被吊装到函数顶部。因此,要避免这种混乱,最好的方式就是在前期声明所有打算使用到的变量。

  13. 创建高效的for循环:使用for循环来遍历javascript集合,应将集合的长度缓存到一个变量,并使用改变量作为for循环的条件,例如:

    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.   for (var i = 0; i < myarray.length; i++) { //antipattern use of collection length  
    2.       // do something with myarray  
    3.  }  
    4.   
    5. //Right pattern  
    6. var arrLength =  myarray.length;  
    7. for (var i = 0; i < arrLength; i++) {  
    8.      // do something with myarray  
    9. }  

  14. 使用hasOwnProperty:在遍历对象的属性的时候,使用hasOwnProperty方法来过滤原型链属性是相当重要的,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.   var man = {hands: 2, legs: 2, head: 1};  
    2.  //somewhere else in the code  
    3. // a method was added to all object  
    4. If( typeof Object.prototype.clone === undefined){  
    5.         Object.prototype.clone = function(){};  
    6. }  

    在定义了man之前,对象原型添加了一个有用的名为clone的方法。原型链的存在意味着所以对象都会自动获得该新方法。为了避免在枚举man的时候显示clone方法,就需要调用hasOwnProperty方法来过滤原型方法,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. // 1.  
    2.    // for-in loop  
    3.    for (var i in man) {  
    4.           if (man.hasOwnProperty(i)) { // filter  
    5.                  console.log(i, ":", man[i]);  
    6.           }  
    7.     }  
    8.     /* 
    9.        result in the console 
    10.        hands : 2, legs : 2, heads : 1 
    11.     */  
    12.     // 2.  
    13.    // antipattern:  
    14.   // for-in loop without checking hasOwnProperty()  
    15.    for (var i in man) {  
    16.         console.log(i, ":", man[i]);  
    17.    }  
    18.   /* 
    19.    result in the console 
    20.    hands : 2, legs : 2, heads : 1, clone: function() 
    21.  */  



  15. 使用===代替==:javascript在比较隐式类型变量的时候,会进行类型转换,而这也就是为什么在比较“false== 0”或“"" == 0”时返回true的原因。为了避免因隐式类型转换所造成的混乱,在比对值与不同的类型表达式的时候,应始终使用===或!==操作符来进行检查。
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  var zero = 0;  
    2. if (zero === false) {  
    3.       // not executing because zero is 0, not false  
    4. }  
    5. // antipattern  
    6. if (zero == false) {  
    7.       // this block is executed...  
    8. }  

    另一学派的观点是,当使用==已经足矣的时候,使用===就显得有掉多余,例如,当使用typeof的时候,明确返回的是字符串,这时候就没必要使用恒等。不过JSLint要求严格的相等,它要求代码保持一致的外观以及减少阅读代码的精力(这个==是故意还是遗漏?)。

  16. 不要使用eval:一定要记住“eval是魔鬼”这句口头禅。这个函数可接受任意的字符串,并将字符串作为javascript代码执行,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  console.log(typeof un); // "undefined"  
    2. var jsstring = "var un = 1; console.log(un);";  
    3. eval(jsstring); // logs "1"  
    4. console.log(typeof un); // "number"  

    因此,在这里使用eval会将un定义为一个数字直接量。假如把jsstrig的值作为用户的输入值,这将会导致不少的安全问题。所以,使用eval会带来安全隐患。

  17. parseInt的正确用法:正确使用parseInt的redix可以避免一些不必要的结果,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  alert(parseInt("8")); // "Will print 8"  
    2. alert(parseInt("08")); // "Will print 0"  
    3. alert(parseInt("08,10")); // "Will print 8"  
    4. If we use parseInt(“08”), it gives octal value of 08.  
    因此,要永远记住使用第二个变量,即redix,来定义数字的类型,无论它是十进制、八进制还是十六进制。

  18. 小心函数吊装:该问题类似于上面讨论的变量吊装。他们唯一的区别是只在使用函数声明才会发生,而不是匿名函数。函数声明这种情况,函数声明被吊装时,不单只是声明问题,处理不当就会出现一些非预想的结果,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  // antipattern  
    2. // for illustration only  
    3. // global functions  
    4. function foo() {  
    5. alert('global foo');  
    6. }  
    7. function bar() {  
    8. alert('global bar');  
    9. }  
    10. function hoistMe() {  
    11. console.log(typeof foo); // "function"  
    12. console.log(typeof bar); // "undefined"  
    13. foo(); // "local foo"  
    14. bar(); // TypeError: bar is not a function  
    15. // function declaration:  
    16. // variable 'foo' and its implementation both get hoisted  
    17. function foo() {  
    18. alert('local foo');  
    19. }  
    20. // function expression:  
    21. // only variable 'bar' gets hoisted  
    22. // not the implementation  
    23. var bar = function () {  
    24. alert('local bar');  
    25. };  
    26. }  
    27. hoistMe();  

    在示例中,会看到就像普通变量一样,无论foo或bar自身位于hoistMe的任何地方,都会将他们移动到顶部,重写全局的foo和bar。不同的地方是本地(local)的foo定义被吊装到顶部并能正常工作,尽管它是在后面定义的。而bar的定义则不会被吊装,只是进行了声明。这就是为什么直到代码执行到bar的定义,会发现它是undefined且不能作为函数使用(同时阻止了全局的bar出现在作用域链中)。

  19. 将属性添加到全局命名空间:在将一个属性添加到全局命名空间或全局对象的时候,要小心,或许这已经存在了,可能会覆盖掉他们。因此,在添加一个属性或创建命名空间之前,最好先检查一下它是否存在,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  // unsafe and antipattern  
    2. var MYAPP = {};  
    3. // better  
    4. if (typeof MYAPP === "undefined") {  
    5. var MYAPP = {};  
    6. }  
    7. // or shorter  
    8. var MYAPP = MYAPP || {};  

    为了避免在全局命名空间创建任何成员时编写这些样板代码,可以创建一些可重用的代码,将要创建的对象传递过去,让代码去执行上述验证并决定添加或放弃对象。

  20. 利用JSLint:JSLint会获取javascript源代码并扫描他们。如果发现问题,它会返回问题描述和大致位置的消息。问题不一定是语法错误,尽管经常会是。JSLint专注于一些风格约定和结构问题。这并不能说明你的程序是正确,只是提供一些方式来协助发现问题。只需要简单的粘贴脚本,它就可以快速的扫描代码并找出任何明显的问题和错误。
    网址:http://www.jslint.com/

  21. 避免使用with语句:乍看上去,With语句看上去不赖,主要原因是它可以提供一种简单方式来访问嵌套很深的对象,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. with (being.person.man.bodyparts) {    
    2.                                arms = true;    
    3.                                legs = true;    
    4.                 }  

    代替:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. being.person.man.bodyparts.arms = true;    
    2.                being.person.man.bodyparts.legs= true;  

    不幸的是,经过一些测试后,会发现他们在设置新成员时的表现非常糟糕,替代方法是,可以使用var:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.                   var o = being.person.man.bodyparts;    
    2.                 o.arms = true;    
    3. o.legs = true;  


  22. 使用Ext JS内部函数来添加或移除Ext JS组件的元素:要将一些对象添加到Ext JS容易,不建议使用以下代码:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. componentObj.items.items[0] = newObject;  

    以上代码的问题:如果使用以上代码将一些东西添加到容易,Ext JS的所有内部任务就不会在将对象添加到容器时执行,而这将会引起一些问题。

    解决:应使用容器的add方法,或使用组件其他类似的内部函数来处理。

  23. 从Ext JS的组件中返回组件:要从Ext JS的组件中返回对象,不要使用以下代码:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. var anObj = componentObject.items.items[0].items.items[1];  

    问题:如果在上述层次结构加入了一个新的层,如一个面板或容器,那么整个层次结构就会变得不稳定,可能会返回错误的结果。

    解决:使用Ext.getCmp或queryById来代替过长的层级遍历。尽管这比层级遍历要慢,但可保证不会出现以上所说层次问题。

    (译者注:在4中,可使用down或up方法查询子组件或父组件)

  24. 编写可读、可调试和可重用的代码:在编写代码时,开发人员应创建短小且有意义的类或函数。不要试图去把大量的代码写在一个单独的块中。尽量保持代码块短小,从而让它跟易于重复使用,且更易于阅读。

  25. 使用get/set方法:如果使用config配置项来定义类的变量,Ext JS会自动创建变量的修改方法(setter)和访问方法(getter),使用这些修改方法和访问方法可提供代码的可读性。如果使用这些方法来而不是直接使用变量名,还可以重写这些方法或为这些方法添加功能。因此,应尽可能使用访问方法和修改方法作为类这层的变量并使用他们。

  26. 永远不要使用自动布局来创建组件:一些常见的错误就是因为Ext JS开发人员在创建容器的时候忘记为他们定义布局了。尽管这在一些浏览器上或特定版本的浏览器会运行得很好,但从长远来看,这会导致某些部件出现黑屏或失真等问题,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  Ext.create(‘Ext.panel.Panel’,{  
    2.                 height: 200,  
    3.                 width: 500,  
    4.                 //layout: ‘hbox’,          // anti pattern if layout is not defined  
    5.                 items:[  
    6.                                 {  
    7.                                                 xtype: ‘button’,  
    8.                                                 id: ‘button1’  
    9.                                                 flex: 1  
    10. },{  
    11.                                                 xtype: ‘button’,  
    12.                                                 id: ‘button2’  
    13.                                                 flex: 1  
    14. }  
    15. ]  
    16. });  

    在这里,在面板中没有定义布局,面板的高度和宽度将不会根据它的组件的flex值进行分配。原因就是容器的布局不知道如何将容器的高度和宽度分配给容器的组件。如果把“layout:hbox”的注释去掉,这两个按钮将会根据父容器调整宽带和高度。

  27. 避免使用固定的高度和宽度:千万不要在应用程序中固定任何容器或组件的高度和宽度,因为这会破坏应用程序的布局流动性。尽量将最顶层的容器或viewport的高度和宽度定义为屏幕大小,然后小心的通过正确的使用flex或布局将这个尺寸分分派给子组件或子容器,不要使用硬编码的高度或宽度,也可以通过使用Ext JS的功能来计算,如Ext.getBody().getViewSize().height或Ext..getBody().width:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. Ext.create(“Ext.panel.Panel”,{  
    2.                height: Ext.getBody().getViewSize().height,  
    3.                width: Ext.getBody().getViewSize().width  
    4. );  

  28. 正确使用flex关键字:flex是一个用来保持流动性布局的非常重要的关键字,该值通常用来决定如何接收父容器的尺寸,例如,如果两个组件的定义分别为flex=1和flex=2,那么,这两个组件从父容器接收到的尺寸比例就是1:2。在这里,还需要留意另一个重点,就是该值只用来划分父容器的宽度或高度,而不能同时来决定子组件的宽度和高度,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  Ext.create(‘Ext.panel.Panel’,{  
    2.                 height: 200,  
    3.                 width: 500,  
    4.                 layout: ‘hbox’,           
    5.                 items:[  
    6.                                 {  
    7.                                                 xtype: ‘button’,  
    8.                                                 id: ‘button1’  
    9.                                                 flex: 1  
    10. },{  
    11.                                                 xtype: ‘button’,  
    12.                                                 id: ‘button2’  
    13.                                                 flex: 1  
    14. }  
    15. ]  
    16. });  

    以上代码,使用了水平盒子布局,因而,只有按钮的宽度会根据flex值的比例进行划分,而两个按钮的高度则会是100%,即是每个按钮的宽度会是250,而高度是200。同样,如果将面板的布局修改为垂直盒子布局,那么,每一个按钮的宽度将会是500,而高度会是100。因此,要适当并谨慎地使用flex来实现更好的流动布局。

  29. 尽量少用minxWidth、maxWidth、minHeight和maxHeight:尽量在需要的时候才使用这些属性,否则就不要使用他们,因为在组件布局方面,他们的使用是非常昂贵的。如果在组件中碰到这些属性,布局会重新进行计算,因此,这样的代价非常昂贵。

  30. 使用Sencha Cmd工具来压缩(minification)代码:进行压缩处理时,会消除空白、注释和javascript代码中其他不重要的部分,这样可减少javascript文件的大小,从而减小服务器到浏览器的传输量。另外,压缩处理还会重命名变量为较短的名称(但只有在它是安全的时候),如前面代码中的参数D、C、B或A。压缩处理只会重命名局部变量,因为重命名全局变量可能破坏代码。这也是为什么使用局部变量是一个好做法的主要愿意。如果在一个函数内,使用全局变量,如DOM引用,超过一到两次,那就给它分配一个局部变量,这是好的做法。这些通常需要一些工具(压缩)来实现,如Ext JS命令行工具(只适用于Ext JS的应用程序开发)、雅虎的YUICompressor或谷歌的Closure Complier(适用于普通的javascript应用程序),这哟这样加快页面加载时间。压缩为生产准备的脚本是十分重要的,因为最终可以将文件的大小大大压缩,通常可以压缩一半的大小。

  31. 保持DOM的轻量化:尽量保持DOM的轻量化可加快DOM的访问和维护速度。这可通过移除不必要的DOM元素来实现,例如,如果使用卡片布局,且每个卡片都要显示大量容器或组件,而且用户不会返回之前的卡片,最好的方式就是在程序(这样所需的容器或组件就不会再次创建)中缓存DOM元素,然后将DOM
    元素从DOM中移除以让它轻量化。

  32. 尽量减少DOM访问:DOM访问是昂贵的,它是javascript性能最常见的瓶颈。这是因为DOM的实现通常是与javascript引擎分离的。底线就是应将DOM访问减少到最低限度,这意味着:
    • 避免在循环中进行DOM访问
    • 将DOM引用指派给局部变量并使用举报变量进行操作
    • 在可能的情况下,使用API的选择器
    • 当要遍历HTML集合时,缓存集合的长度
    • 尽量使用Ext JS的方法来访问或维护DOM,以获得跨浏览器支持,否则某些代码在某些浏览器可能会失败,这是因为每个浏览器都有它自己的DOM访问方式。
  33. 不要创建内联处理函数:千万必要创建内联函数,因为对于回调会监听所使用的函数来说,是常见的问题源。他们不单需要花费时间来创建,还需要昂贵的闭包,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  Ext.define(‘MyClass’,{  
    2.                 constructor: function(config){  
    3.                                 this.store.on(‘load’, function(){  
    4.                 ………..;  
    5. }, this);  
    6.                 }  
    7. });  

    以上代码,每当构造函数执行一次,就会创建一个新的监听函数,这不单需要花费时间处理闭包问题,还潜在内存泄漏问题。以下是首选的模式:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  Ext.define(‘MyClass’,{  
    2.                 constructor: function(config){  
    3.                                 this.store.on(‘load’, this.onStoreLoad, this);  
    4.                 },  
    5.                 onStoreLoad: function(){  
    6.                                 ………;  
    7. }  
    8. });  

    在以上代码中,监听函数只会创建一次并保存在类中。每个实例都会共享相同的函数。创建函数的开销只有一次,而且没有闭包泄漏的风险。更重要的是,这样的编码风格,还可以让子类轻松的去重写监听。

  34. 正确使用xtype:一个常见的神话就是xtype可通过延迟初始化提高性能。通常,这不是真的。尽管通过xtype配置项将组件添加到容器,它还是进行实例化,尽管它还不可见。虽然xtype有很大用途,但对于应用程序的性能或资源消耗来说没有实际可衡量的影响。

  35. 千万不要使用整个ext-all.js库:通常,ext-all.js是包含了全部功能和组件的巨大编码集,但我们很少会使用到Ext JS的全部功能和组件,因此,没必要在只使用了60%到70%代码的时候去加载一个巨大的文件。可以使用Sencha命令行工具来压缩生产代码,它会从ext-all.js中复制所需的类并粘贴到结果all-classes.js。如果没有使用Sencha命令行工具,还可以通过其他方式来确保不用加载整4个ext-all.js文件。

  36. 批处理任务:尽量使用批处理进行更新,以便渲染只在最后时刻触发一次渲染。例如,修改记录的字段值会立即更新到网格,如果想让几个字段的更新更高效,就进行批处理,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  record.beginEdit();  
    2. record.set(‘name’, ‘Tom’);  
    3. record.set(‘age’, 17);  
    4. record.set(‘member’, true);  
    5. record.endEdit();  

    如果要修改的字段更多,效果会更明显。
    另一个例子是将组件添加到容器,每添加一次,容器就会重新计算布局一次。将多个组件传递给同一个add方法,将会高效得多,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  //slow code  
    2. container.add(panel);  
    3. container.add(button);  
    4. container.add(grid);  
    5.   
    6. //fast  
    7. container.add(panel, button, grid);  
    8.   
    9. //using an array is also fast  
    10. container.add([panel, button, grid]);  

    在没有其他批处理机制的时候,可以使用暂停布局来临时辅助实现:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  container.suspendLayout = true;  
    2. doSomethingToChangeLotsOfLayoutChange();  
    3. container.suspendLayout = false;  
    4. container.doLayout();  



  37. 使用deferredRender:在使用卡片布局或标签面板的时候,要认真考虑一下deferredRender选项。当将它设置为true,就可以将隐藏的条目或选项卡实现延迟渲染。只有在隐藏的条目第一次显示的时候,才会进行渲染。当设置为false,所以的卡片或标签就会作为容器渲染的一部分在同一时间进行渲染。
    延迟渲染通常来说挺好的,这有助于将渲染过程分解为较小的数据块,不会对用户造成明显的延迟。不过,渲染所有的卡片或标签页,在隐藏的卡片或标签页第一次显示的时候有助于改善UI的响应。

  38. 网格的缓冲渲染:在使用网格的时候,如果数据集很打,使用缓冲渲染吧。这意味着在加载存储的时候,存储要维持一个“预读”缓冲,以保存将要使用到的记录页。
    如果数据少于100000行(也取决于每行的列数),最好的方式是在客户端将所有数据保存到预读缓冲区。
    渲染大的HTML表格会谋杀性能。这事实上只是HTML表格算法,只保持所以数据应该不会导致问题。
    要做的只是滚动的时候,在“适当时候”渲染表格的行。要做到这一点,只需要一次加载预读缓冲中的所有数据。这样做后,就可以从预读缓存中加载存储的主要数据,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  myStore = ……{  
    2. ………….,  
    3. Buffered: true// Tell grid to use a paging scroll manager to scroll and refresh table automatically.  
    4.                                 perPageCount: 0 // never remove pages from the prefetch buffer;  
    5. }  
    6.   
    7. myStore.prefetch({  
    8.                                 start: 0,  
    9.                                 limit: 999999,  
    10.                                 callback: function(){  
    11.                                 myStore.load(0, 49);  
    12. }  
    13. });  

    预读所有所预期的数据集后,回调会加载存储的主缓存(映射到网格面板内的缓存)所使用的50行数据。由于只能得到小于50行的表格,所以在滚动接近视图边界的时候,它就会更新。该技术在4.0.x工作OK,不过在4.1.0进行了翻修,变得更快和更平滑。在网格目录的缓冲网格示例延时该技术。
    要注意的是,太多的列会对HTML的性能产生影响。
    元素太多也会这样,因而需要避免显示不需要的列。

  39. 在标签面板的每个标签页的beforeRender事件处理中填充标签页:在beforeRendere中填充标签页是更好的方法。正确的实现这个可以有效的改进包含大量未渲染组件的应用程序的性能。虽然实例化一个组件的时间比渲染或布局所需要的时间要小,但如果有大量的未渲染组件也需要时间。带有许多标签页的标签面板这样处理会更理想。要特别支出的是,我建议的只是shuffle time around:cut the time taken for the initial load in favour of slightly slower tab transitions.这只是你武器库中的另一种技术。与以往一样,优化的黄金规则是在实现之前,要做一些有意义的测量。

  40. 使用mon代替on:这两个关键字都是用来事件处理绑定到事件的。不过,使用mon而不是on会更高效,因为这可确保监听会在MyClass的实例被销毁的时候自动移除它,例如:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  Ext.define(‘MyClass’,{  
    2.                                 Constructor: function(config){  
    3.                                                 //this.store.on(‘load’, this.onStoreLoad, this); // this is not good  
    4.                                                 this.store.mon(‘load’, this.onStoreLoad, this);  
    5.                                 },  
    6.                                 onStoreLoad: function(){  
    7.                                                 ………;  
    8. }  
    9.   
    10. });  

  41. 在事件处理程序中不要保留对this的引用:通常,在任何Ext JS组件的内联事件处理函数工作时,都需要使用this关键字来获取相应类的对象的应用。通常情况下,组件的事件处理程序的this指向的是该组件这个对象。隐藏,要解决这个问题,一些开发人员会在构造函数或类的initComponent方法内使用一些全局变量来引用类,然后在内联处理中使用全局变量来获取相应的类对象,不过,这是错误的做法,我们赢遵循这里的约定。

  42. 尝试使用客户端缓存:本地存储、session存储、sqllite等等。如果使用HTML5,就可以将一些数据保持在客户端(不是cookie),并在再次浏览该页时使用这些数据。这样,就可以避免将一些没必要的东西发送到服务器或数据库。在Ext JS,可以使用代理来处理这类缓存。在Ext JS中有两类代理:ClientProxy和ServerProxy。ClientProxy就是用来处理客户端缓存的。有以下三种类型的ClientProxy:

    • Ext.data.proxy.LocalStorage:LocalStorageProxy使用新的HTML5的localStorage来加载和保存数据到客户端浏览器。LocalStorage可在域中设置字段,这意味着关闭浏览器后再打开它,数据还在LocalStorage中。LocalStorage允许长期存储,此外还可以在所有浏览器的标签页或窗口中访问它。


    • Ext.data.proxy.SessionStorage:SessionStorageProxy使用的是新的HTML5的SessionStorage API来加载和保持数据到客户端浏览器。SessionStorage设置的字段是基于窗口的,这意味着浏览器关闭后,数据也会丢失,即使网站在另一个浏览器窗口依然是打开的。SessionStorage的数据被局限于创建他的浏览器窗口中。

    • Ext.data.proxy.MemoryProxy:MemoryProxy是一个辅助性的代理。通常,它用来加载一些内联数据到存储。MemoryProxy的内容会在页面刷新后对视。它通常用来加载临时数据。

    除了这些,一些浏览器,如chrome,还支持一些特殊类型的客户端数据,如SQLLite,它可以在客户端存储一些数据。因此,通过使用这些技术,可以用来提高应用程序的性能。

  43. 不要使用Array.isArray:Array.isArray(anyObject)方法用来判断anyObject是否一个数组对象。该方法只能在Chrome中工作,不能在IE中工作。IE并不能识别该方法并会给出错误。可使用instanceof来替代该方法来达到相同的目的。instanceof方法既可在IE工作,也可在Chrome工作。

  44. 一定要注意日期格式:在解析日期的时候,要注意日期字符串的格式是否既支持IE,又支持chrom:
    [javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1.  var dateString = "03/20/2008";                                   
    2. var dateString = "2008/03/20";                                                   
    3. var dateString = "03-20-2008";                                    
    4. var dateString = "March 20, 2008";                                           
    5. var dateString = "Mar 20, 2008";                                                
    6. var dateString = "Oct 2, 2011";  

    不要使用var dateString = "Oct/02/2011",IE不支持,在chrome中是支持的。我已经在应用程序修正了这个。

  45. 别忘了以下要点:以下是在将代码发布为产品时不应忘记的一些要点:

    没有尾随逗号
    即使只有一行注释,也要使用多行注释来代替单行注释
    没有调试的代码要留下注释
    在预期的情况下,不要丢失任何分号
    不要在生产代码中使用console.log
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值