EXT组件化.docx
《EXT组件化.docx》由会员分享,可在线阅读,更多相关《EXT组件化.docx(85页珍藏版)》请在冰豆网上搜索。
![EXT组件化.docx](https://file1.bdocx.com/fileroot1/2023-4/21/a840df1f-7b6e-4c25-a64d-5590cf3dd642/a840df1f-7b6e-4c25-a64d-5590cf3dd6421.gif)
EXT组件化
目录
前言2
EXTJS的缺点以及优点3
EXTJS面向对象3
EXTJS组件化编程5
EXTJS组件之间的通信16
EXTJS中的Singleton19
EXTJS扩展组件例子20
EXTJSJavaScript的调试28
EXTJSXTemplate29
EXTJSDataView的使用31
EXTJS优化服务器端41
EXTJS与后台的通信(Java)41
EXTJS在AIR中继续施展53
前言
本人学习编程一年,实际开发一年。
对编程的理解还仅处于很肤浅的认识上,本文本没有打算公开出来。
但是,我一直认为与人分享劳动成果是一件非常开心的事情,不仅能让更多的新手分享到我学习的快乐,而且从中我还能学到更多的知识。
由于本文最初是一个个人的总结,后来觉得还是公开出来的好,即使自己有错,也心甘情愿的挨板砖。
落后就要挨打,挨打也得站直了。
由于我确实很菜,所以文中必然有一定的理解性错误,如果本文有幸被您看到,请切不可认定这就是问题的答案。
希望能够收到更多的板砖,这样我才能从自我的误区中走出来,如果您是新手,最好不要把这篇文章此作为技术手册,误人子弟的罪名我担当不起。
因为很多个人的误解不经过无数的板砖是无法自我发现的。
这个文章的语言比较晦涩,我也尽量的想把一些问题阐述的更清晰一些,但是无奈我现在才发现语文的重要性……
写了这么多东西,希望有人能给我的文中错误指出,中国的开源离不开您的指点,这样我才能更好的去完善它,也让我更有信心的去开源。
我的邮箱andy_ghg@。
希望能够得到高手的指点。
也希望结交到更多的朋友。
EXTJS的缺点以及优点
EXTJS是有缺点的,缺点在于,由于过度的封装,导致我们很难对其样式有较大的改动,即使有所改动也很难达到预期的目标。
EXTJS的优点也是显而易见的,他有着丰富的控件库,有着丰富的工具包,有着优美的语法。
EXTJS很容易让一个有Swing或者Winform编程经验的人上手。
有很多人甚至根本不用知道XMLHttpRequest和ActiveXObject就可以轻易的编写AJAX程序。
但是EXTJS是基于AJAX技术的,如果你不对AJAX有一个全面的了解,你可能会走不少的弯路,最终可能是知其然而不知其所以然。
由于EXTJS的过度封装,导致其组件的灵活性有所下降,在改变外观样式的时候,也经常出现弄巧成拙的事情。
还有最重要的一点就是EXTJS体积,EXTJS的CSS样式表文件加上主要的库文件大约能达到800KB,这还不包括他的一些图片文件。
所以这方面还是EXTJS应该改进的地方。
EXTJS面向对象
关于JavaScript的变量、对象以及一些基础知识,大家可以在《AJAX入门经典》(英文名BeginningAJAX)这本书中的第二章看到简单的描述(更详细的就直接去买本JS相关的书籍吧)。
这里不再阐述。
在EXTJS中,我们该如何管理好我们的对象?
对象的管理,在以往的EXTJS开发中,最初的写法可能是面向过程的,如下:
那么在这里,位于Panel外面的对象就不会因为panel的销毁而被销毁,它可以一直存在于内存当中。
在开发中如果有多个这样的对象,那么管理起来是一个很耗费时间精力的。
这主要体现在项目后期,发布阶段,项目发布时,JS代码不应再像开发时那样分散在各个JS文件当中,而是代码的整合,全部归并到一个JS文件并扰码压缩,减少JS代码的体积提升JS代码的加载速度(最明显的可以看到ext-all.js的体积是621KB,而ext-all-debug.js则达到了2.35MB),这样问题就很严重了,如果我在某个JS文件中定义了一个变量:
那么,这个变量(对象)就不能重名,整个项目中就不能再出现一个与其同名的变量,因此,变量名的维护也是一个浩大的工程,当然,也可以用绝对不重复的变量名,比如加长型的,或者是有标识的,但是这并不能解决根本性的问题。
那么,如何管理好这些模块内部使用的变量?
那么这就是对象化所解决的问题,首先看下面的代码:
varDog=function(){
this.say=function(){
alert("汪汪");
};
}
varsmallDog=newDog();
smallDog.say();
当say这个函数作为Dog这个对象的一个属性存在于Dog对象内部,那么这个方法就不会与外部的方法命名重复。
这是作用域所起到的作用。
按照这个思想,我们稍微改造一下上面的代码:
Ext.onReady(function(){
varpanel=newExt.FormPanel({
items:
[newExt.form.TextField()]
})
})
将textfield放入到对象的构造方法内,那么就不会造成变量名冲突等事故的发生。
但是这只是一个简单的Panel,如果我们所需要的是一个比较大的模块怎么办?
总不能在每次使用的时候都写一大堆这样的代码吧?
而且我们在这里仅仅是将一个输入框放在了表单内,如果我们要实现一个组件又该如何去写呢?
那么我们现在假设有一个需求,在登录中显示类似下面的验证码:
那么,这个东西就不是简单的在items里new几个textfield就能解决问题的。
首先,我们来拆分一下这个登录窗体:
那么在验证码这块,我们需要一个Column布局的Panel(最好是Container),在这个Panel中,我们还需要两个Panel,一个是左边的验证码输入,一个是右边的验证码显示。
如果按照上面的那种对象化去写,必然会造成代码的混乱和难以理解。
会对后期的维护造成不必要的麻烦。
那么,我们该如何去完成这个组件的编写呢?
这就是组件化所要完成的任务。
EXTJS组件化编程
通过查看EXTJS的源码(最简单的是Ext.Window组件),会看到ExtJS最常用的一种对象创建方式:
它是通过继承来实现了对象的封装,ExtJS最底层的那个基类是Observable,它提供了最基础的事件机制,通过层层的封装与扩展,它的派生类中是越来越庞大,所提供的功能也越来越多,就比如Window这个组件,它继承自Panel并添加了默认的resizabel,closable,draggable等属性(Panel也可以加入类似的函数,但是需要编码实现)。
而Panel又继承自Container,并加入了渲染Title和tbar等功能。
那么,我们自己的对象也可以完全模仿这种语法进行创建如:
上面的程序会输出World,而且在看到上面的语法之后,会想到什么?
对,是Java等后台语言最常见的vo对象写法,看init函数,它就是一个构造函数,而下面则是get和set函数,不同的在哪里?
那么就是prop这个属性其实在外面是可以随意调用的,无需使用get和set函数也能对其值进行修改(JavaScript就是这么“弱”……),而且init构造函数不能被重载。
题外话:
EXT的extend函数的本质是封装的Prototype原型继承,而JavaScript的delete操作符是不可以操作Prototype原型方法函数的。
所以在此处通过delete操作符是无法正常删除对象属性的从而达到提高效率的预期目标。
但是我们也有办法对其进行私有化处理!
我们还有闭包。
(研究不深,在此不发表言论)。
在这里不是要完全模仿Java等后台语言,而是就这个现象进行扩展,扩展成我们自己的对象,那么基于这个事例,我们应当可以很简单的就开发出属于我们自己的组件。
例如,我们要创建一个最基本最常用的登录窗口,我们在这里将其称之为一个模块,这个模块虽然小了点,但是他具备所有模块都应当具备的特征,代码如下:
Ext.namespace("Gh.LoginModule");
/**
*@author葛昊龑
*@classGh.LoginModule
*@extendsExt.Window
*/
Gh.LoginModule=Ext.extend(Ext.Window,{
title:
"LoginSystem",
draggable:
false,
closable:
false,
resizable:
false,
mode:
true,
layout:
"fit",
autoHeight:
true,
width:
270,
/**
*@description重写父类的初始化函数
*/
initComponent:
function(){
//使用superclass来获取父类的初始化函数,类似Java中的super
Gh.LoginModule.superclass.initComponent.call(this,arguments);
this.usernameField=newExt.form.TextField({
fieldLabel:
"YourAccount"
});
this.passwordField=newExt.form.TextField({
fieldLabel:
"YourPassword"
});
this.loginForm=newExt.FormPanel({
frame:
true,
autoHeight:
true,
defaults:
{
anchor:
"95%"
},
items:
[this.usernameField,this.passwordField]
});
this.add(this.loginForm);
this.addButton("Submit",this.doLoginHandler,this);
this.addButton("Regist",this.doRegHandler,this);
},
doLoginHandler:
function(){
//TODOSomthing
},
doRegHandler:
function(){
//TODOSomthing
}
});
Ext.onReady(function(){
varloginWin=newGh.LoginModule();
loginWin.show();
})
效果图
那么,这样,我们实际上就利用继承来完成了我们的第一个组件--登录组件。
EXTJS对象化的特征是什么?
1.封装---变量的作用域被固定在对象内部,内部的对象将会随着对象的销毁而被销毁(虽然垃圾回收不会主动来回收,但是这一步很重要)。
封装了一些内部操作,如上面的doLoginHandler和doRegHandler,总所周知,在JavaScript中Function是作为一个特殊对象存在的,因此,这两个函数也都只存在于这个对象内部。
而不会影响到外部的变量。
2.继承---继承了Window,拥有了Window所拥有的一切属性,并为Window添加了一些东西,例如一个表单
3.多态---我可以定义其默认的closable为false,让其无法被关闭,为其添加了标题,让其拥有了默认的标题,我甚至可以让他变成一个不可以拖拽、不可以改变大小、不可以被关闭的系统登录窗口(姑且就这么理解成多态吧)。
其实做到这一步应该已经可以了,这就是第一个使用组件化方式编写的对象,但是看看这个对象哪里还有不足之处?
那就是一个字:
乱。
因为这个对象也仅仅是将原先定义在外部的变量统一规划到了一个对象下,只是做到了变量统一化,但是还没有达到代码逻辑与视图的分离,这里仅仅是一个登录组件,如果这个组件是一个相当大的组件,例如OA中的工作流系统的某个场景,它不仅仅只是一个表单,它还包括一个流程图,一个Grid(或者其他)还有一两个表单,甚至还有可能会出现一个Tree,那么如果全部按照上面这种对象化的写法去做,会又出现代码与逻辑混杂,变量声明不清晰,组件与组件之间界限模糊。
如何解决这个问题?
我们来稍微改造一下这个登录窗口的写法,我们将Form表单从InitComponent函数中抽取重来放到外面去:
Ext.namespace("Gh.LoginModule");
/**
*@author葛昊龑
*@classGh.LoginModule
*@extendsExt.Window
*/
Gh.LoginModule=Ext.extend(Ext.Window,{
title:
"Login",
draggable:
false,
closable:
false,
resizable:
false,
mode:
true,
layout:
"fit",
autoHeight:
true,
width:
270,
/**
*@description重写父类的初始化函数
*/
initComponent:
function(){
//使用superclass来获取父类的初始化函数,类似Java中的super
Gh.LoginModule.superclass.initComponent.call(this,arguments);
this.add(this.getLoginForm());//只负责布局
this.addButton("Submit",this.doLoginHandler,this);
this.addButton("Regist",this.doRegHandler,this);
},
/**
*@description初始化表单对象
*@return{@linkExt.FormPanel}
*/
getLoginForm:
function(){
returnthis.loginForm||(function(){
varusernameField=newExt.form.TextField({
fieldLabel:
"YourAccount"
});
varpasswordField=newExt.form.TextField({
fieldLabel:
"YourPassword"
});
this.loginForm=newExt.FormPanel({
frame:
true,
autoHeight:
true,
defaults:
{
anchor:
"95%"
},
items:
[usernameField,passwordField]
});
returnthis.loginForm;
}.createDelegate(this))();
},
doLoginHandler:
function(){
//TODOSomthing
},
doRegHandler:
function(){
//TODOSomthing
}
});
Ext.onReady(function(){
varloginWin=newGh.LoginModule();
loginWin.show();
})
效果是一样的
我们可能会注意到getFormPanel这个函数的写法,是的,这是一个简单的Singleton,form在第一次被初始化之后,则不会再创建一次form表单对象,直到form被销毁(不存在了)为止。
那么按照这种写法,我们来解决上一节所提出的那个需求(最后的那个登录窗口):
这个登录窗体,我们需要验证码,那么如何依照组件化编写方式编写这个组件呢?
按照上面的那种写法,我们开始组织我们的代码:
Ext.namespace("HRM.Common.Login");
/**
*@authorandy_ghg@">葛昊
*@description登陆组件
*@version1.0
*@classHRM.Common.Login
*@extendsExt.Window
*/
HRM.Common.Login=Ext.extend(Ext.Window,{
closable:
false,
resizable:
false,
draggable:
false,
modal:
true,
title:
"登陆",
width:
300,
autoHeight:
true,
layout:
"fit",
PROJECT_NAME:
"/HRM/",
initComponent:
function(){
HRM.Common.Login.superclass.initComponent.call(this,arguments);
this.addEvents("loginSuccess");
this.add(this.getFormPanel());
this.addButton("登陆",this.loginBtnHandler,this);
},
/**
*获取登陆表单
*@return{@linkExt.FormPanel}
*/
getFormPanel:
function(){
returnthis.formPanel||(function(){
this.formTop=newExt.Panel({
layout:
"form",
defaultType:
"textfield",
defaults:
{
anchor:
"95%",
allowBlank:
false,
vtype:
"alphanum"
},
labelWidth:
55,
items:
[{
fieldLabel:
"用户名",
emptyText:
"请输入您的账号名称",
name:
"username"
},{
fieldLabel:
"密码",
name:
"password",
inputType:
"password"
}]
})
this.formPanel=newExt.FormPanel({
frame:
true,
autoHeight:
true,
items:
[this.formTop,this.getValicodeImage()]
});
returnthis.formPanel;
}.createDelegate(this))();
},
/**
*获取验证码图片
*@return{@linkHRM.External.Image}
*/
getImage:
function(){
returnthis.valiImage||(function(){
this.valiImage=newHRM.External.Image({
height:
20,
width:
60,
imageSrc:
this.PROJECT_NAME+"servlet/ValicodeServlet"
});
returnthis.valiImage;
}.createDelegate(this))();
},
/**
*获取验证码输入部件
*@return{@linkExt.Panel}
*/
getValicodeImage:
function(){
returnthis.valicode||(function(){
this.valicode=newExt.Panel({
layout:
"column",
items:
[{
xtype:
"panel",
columnWidth:
.7,
labelWidth:
55,
layout:
"form",
items:
[{
xtype:
"textfield",
anchor:
"100%",
fieldLabel:
"验证码",
name:
"valicode"
}]
},{
xtype:
"panel",
columnWidth:
.3,
items:
this.getImage()
}]
});
returnthis.valicode;
}.createDelegate(this))();
},
/**
*登陆按钮处理函数
*/
loginBtnHandler:
function(){
this.getFormPanel().getForm().submit({
url:
this.PROJECT_NAME+"account_login.do",
scope:
this,
success:
function(form,action){
if(action.result.data=="SUCCESS"){
this.fireEvent("loginSuccess");
}else{
Ext.MessageBox.show({
title:
"失败",
msg:
action.result.data,
icon:
Ext.MessageBox.WARNING,
buttons:
Ext.MessageBox.OK,
fn:
function(){
form.findField("password").reset();
form.findField("valicode").reset();
form.findField("username").selectText();
}
});
this.getImage().setImage(this.PROJECT_NAME+"servlet/ValicodeServlet?
rand="+Ext.id());
}
},
failure:
function(form,action){
}
});
},