Visual Studio(下面简称VS) 2008和2010都有从JS库文件获取智能提示的功能,不过直接使用ExtJS的库文件的话,虽然也有提示,但是不全,应该说是大部分没有,主要原因是VS脚本提示功能是根据原生JavaScript对象的结构读取,而ExtJS的对象定义与原生JavaScript对象完全不同,因而很难取得其属性和方法。因而,要完整支持ExtJS的智能提示,就必须将ExtJS对象的属性和方法提炼出来,然后组织成一个VS能读懂的JavaScript对象结构,这样就能实现智能提示了。

要将ExtJS对象的属性和方法提取出来不难,从图1中DOM树中的Ext对象结构,可以看到,只要遍历一次Ext对象就可以取得所有对象的属性和方法。不过,实际上,不是那么容易,原因是在ExtJS的对象中,有些是实例,有些是函数形式的对象,对于实例,其属性和方法直接在对象内,而对于函数形式的对象,其属性和方法却是在原型内,因而要区别对待。
 

《Ext JS权威指南》节选:在Visual Studio中实现Ext JS智能提示_权威指南

 

还有一个重要问题,就是命名空间的问题,如Ext.menu.Item、Ext.data.Store或Ext.layout.container.VBox这些3或4级的类名。采用递归遍历,存在的问题是,判断该对象是JavaScript对象还是ExtJS方式定义的对象存在难度,因而,最好的方式是使用附录中的类名列表,通过它去遍历对象。这样就简单很多了。在列表中,如果对象是实例,使用Ext的isObject方法,其值会是true,否则,该对象就是函数式对象,需要实例化才能使用,因而属性和方法在原型内,不过这里要注意可能存在静态方法,因而为了避免遗漏,还得遍历对象本身的属性和方法。

Ext对象本身需要另外加进去,主要原因是它不同于其他对象那样可直接通过对象本身或原型提取属性和方法,需要把在类列表中的类排除出去,这个可通过对象或函数是否存在“$className”属性解决。

还有对象的别名问题,这个直接从ClassManager对象的maps对象中的alternateToName属性中取得,将别名直接指向对象VS就认识了。

最后的问题是如何将对象输出到页面,这个使用encode方法就可以自动转了。

命名空间的问题,可通过ns方法解决,它会自动根据类名创建对象,只要别和Ext对象发生冲突就行了。

目标明确后,现在可以开始工作,创建一个名称为VS-Ext.html的文件,然后加入以下代码:

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
  2. <html> 
  3. <head> 
  4.     <title>第1章 在Visual Studio中实现智能提示</title> 
  5.     <link rel="stylesheet" type="text/css" href="../Ext4/resources/css/ext-all.css"/>      
  6.     <script type="text/javascript" src="../Ext4/bootstrap.js"></script> 
  7.     <script type="text/javascript" src="class.js"></script> 
  8. </head> 
  9. <body> 
  10.     <script type="text/javascript"> 
  11.         Ext.onReady(function(){           
  12.               
  13.             //建立命名管道  
  14.             Ext.ns("VS.Ext");  
  15.               
  16.             var processPrototype=function(s,d){  
  17.                 //处理静态方法  
  18.                 for(var c in s){  
  19.                     if(s.hasOwnProperty(c)){  
  20.                         d[c]="";  
  21.                     }  
  22.                 }  
  23.                 //处理原型  
  24.                 for(var c in s.prototype){  
  25.                     d[c]="";  
  26.                 }  
  27.             }  
  28.               
  29.             //处理实例  
  30.             var processInst=function(s,d){  
  31.                 var i=0;  
  32.                 for(var c in s){  
  33.                     d[c]="";  
  34.                 }  
  35.             }  
  36.  
  37.               
  38.     //处理Ext对象  
  39.     for(var c in Ext){  
  40.         if(Ext.hasOwnProperty(c)){  
  41.             var p=Ext[c];  
  42.             if(Ext.isObject(p)){  
  43.                 if(!p["$className"]){  
  44.     if( ["buildSettings","versions","lastRegisteredVersion"].indexOf(c)>=0 ){  
  45.             VS.Ext[c]=p;  
  46.                             }  
  47.                         }  
  48.                     }else if(Ext.isFunction(p)){  
  49.                         if(!p["$className"]){  
  50.                             VS.Ext[c]="";  
  51.                         }                         
  52.                     }else{  
  53.                         VS.Ext[c]="";  
  54.                     }  
  55.                 }  
  56.             }  
  57.  
  58.             //枚举对象  
  59.             Ext.Array.each(classList,function(classname){  
  60.         Ext.ns("VS."+classname);  
  61.         var d=classname.split("."),  
  62.             sobj=Ext[d[1]],  
  63.             dobj=VS.Ext[d[1]];  
  64.         if(d.length >= 3){  
  65.             sobjsobj=sobj[d[2]],  
  66.             dobjdobj=dobj[d[2]];  
  67.         }  
  68.         if(d.length == 4){  
  69.             sobjsobj=sobj[d[3]];  
  70.             dobjdobj=dobj[d[3]];  
  71.         }  
  72.         if(sobj && dobj){  
  73.             if(Ext.isObject(sobj)){  
  74.                 processInst(sobj,dobj);  
  75.             }else{  
  76.                 if(sobj.prototype){  
  77.                     processPrototype(sobj,dobj);  
  78.                 }  
  79.             }  
  80.         }  
  81.     })  
  82.               
  83. var html="Ext="+Ext.encode(VS.Ext)+"<br/>";  
  84. //处理别名  
  85. for(var c in Ext.ClassManager.maps.alternateToName){  
  86. html+=c+"="+Ext.ClassManager.maps.alternateToName[c]+"</br>";  
  87. }  
  88.     Ext.getBody().dom.innerHTML=html;  
  89. })  
  90.     </script> 
  91. </body> 
  92. </html> 

代码中,先创建新的命名空间“VS.Ext”,在其内放置对象的属性和方法。

函数processPrototype用于处理非实例的对象,第一个循环主要是遍历静态属性和方法。第二个循环用于遍历原型的属性和方法。

函数processInst用于遍历实例的属性和方法,在这里不能加hasOwnProperty方法检测属性或方法是否其拥有的,不然会找不到属性和方法,除非是在实例创建后加的属性和方法。

接着处理Ext对象,如果属性指向的是对象,还要排除grid、form等对象干扰,因而这个要自己查看一下Ext的源代码,做一下处理。

接着是枚举class.js中的类了,先通过ns方法生成类的命名空间,然后根据小数点拆分一下类名,取得源对象和目的对戏,然后根据isObject确认对象是实例还是非实例,进行相应的处理。

最后是把VS对象内的Ext对象和ClassManager对象内的别名对照表转换为文本,输出到页面。

在浏览器中打开页面,将看到一个JSON格式的输出文本,全选该文本,然后复制到一个名称为Ext.js的文件内,这样,通过该文件就可在VS内实现智能提示了。

要使用把Ext.js,首先将文件复制到项目中。然后根据编辑的文件类型采用不同的方法。

如果是单独的脚本(扩展名为js)文件,在脚本文件头部加入以下语句:

  1. /// <reference path="[相对路径]/Ext.js" /> 

这里一定要注意路径,如果要编辑的脚本文件与Ext.js文件路径相同,语句为:

  1. /// <reference path="Ext.js" /> 

最简单直接的方法,就是在解决方案资源管理器中把Ext.js文件直接拖动到编辑的脚本文件中,会自动生成该语句。

有了该语句后,输入“Ext.”就可以看到如图2所示的智能提示了。

《Ext JS权威指南》节选:在Visual Studio中实现Ext JS智能提示_Ext JS_02

如果找不到ext.jsb2文件(ExtJS4好像以新方法提供,一般情况下没有该文件),也可以使用1.4.2节中生成的Ext.js文件,笔者测试过,效果是一样的。