提到ExtJs,笔者可以说是感触颇深,本人经历了Extjs许多个版本,大版本从2.0开始,我第一次关注它!之后再项目中用到了3.0,4.2,6.0等不同版本,可以说见证了Extjs的发展贺强大之处。
简单解释下什么是Extjs。它是一个高度分装的前端开发语言:javascript。高度分装:是指封装了几乎所有项目上都会用的前端组件,而不是不可拆解的。扩展性相当高,是我见到过最强大的前端框架。之前还用过easyui框架,bootstrap响应式框架,经历了多个项目后,发现Extjs框架还是比较靠谱的,尤其对于一个后台程序员来说,简直就是福音,再不用为了布局而头疼,再不用为了兼容性而烦恼,再不用写大堆的CSS。
下面介绍下Extjs的优缺点
事物都有两面性,有好就有坏。Extjs也是这样。
缺点
1:UI,在外观方面,Extjs无疑完败给bootstrap、jqueryUI,在样式和布局方面Extjs显得十分的生硬。
图上是我做的demo。树形结构,表格,左右布局。
2:Extjs依赖文件过于庞大,压缩后的“ext-all.js”,2M多,此外还有CSS样式文件等。这个问题直接导致很多互联网企业放弃Extjs,在高并发时代,大家都是在最求响应速度,加载过慢无疑使得用户体验很差。所以,Extjs不会出现在门户网站上,出现最多的地方是什么的?没错,企业级办公OA。Extjs的几套样式也是非常专业的OA风格。
优点
其实在上面的介绍中已经提到了很多Extjs的优点,比如说,组件多,实用与各种办公软件,比如说扩展性高,再比如说布局简单,不懂美工的也能做出大气的页面。更多的不说了,有兴趣的研究下Extjs,更值得提的是它的设计模式,读过源码的人一定能感觉出–其乐无穷啊。
废话不说,学习Extjs开始。
很多人说,我是写后台的,页面交给写前端的人去搞吧。而我想说,一个优秀的资深java高手,同样也能写一手好的前端代码。知识学到了就是自己的。
Extjs版本的选择:
1:如果你正在搭建自己公司的办公OA,推荐使用4.X版本,稳定版本。
2:如果之前没有接触过Extjs,单纯的想学习,推荐研究下4.2。
3:如果之前已经了解过Extjs,那么推荐学习最新的版本,目前官网已经发布6.X。
两个大的版本,4.0之后,Extjs提出前端代码规范:MVC设计模式,此MVC非彼MVC,后java设计模式的MVC有一定的区别。
无论是前端代码还是后台代码,都需要逻辑处理。而前端的工作会多一点,没有后台的数据访问,但是多了界面的交互,数据展示等。
Extjs中MVC的诠释:
M:对应java中的实体类(javaBean),Ext需要对后台返回的数据进行二次分装,也是OOP的表现。(代码如下:)
Ext.define("sys.user.model.UserModel", {
extend:"Ext.base.model.BaseModel",
fields: [
{name:"CREATE_TIME",type:"date",dateFormat:"Y-m-d H:i:s"},
{name:"UPDATE_TIME",type:"date",dateFormat:"Y-m-d H:i:s"},
{name:"CREATE_USER",type:"string"},
{name:"CREATE_USER_NAME",type:"string"},
{name:"ACTIVE",type:"int"},
{name:"USER_NAME",type:"string"},
{name:"SEX"},
{name:"LOGIN_NAME",type:"string"},
{name:"PASSWORD",type:"string"},
{name:"EMAIL",type:"string"},
{name:"PHONE",type:"string"},
{name:"IP",type:"string"},
{name:"LAST_TIME",type:"date",dateFormat:"Y-m-d H:i:s"}
]
});
Ext.define:定义一个类,java中使用new关键字。
extend:继承,既然Extjs也模仿java的OOP,那么继承一定要有的,后面还会提到构造。
fields:定义一个类里面的属性集合。
V:视图表现层。java中V代表整个钱前段代码,包括:html、jsp、js、css等。而Extjs中,V表示构建页面的代码,没有逻辑处理、交互的代码。
Ext.define("sys.user.view.UserView",{
alias:"widget.UserView",
extend:"Ext.base.view.BaseGridView",
constructor:function(){
this.selModel=null;
this.forceFit=false;
this.searchFiledName="员工姓名/登录帐号/联系方式/邮箱";
this.columns=[
{header:"员工姓名",dataIndex:"USER_NAME"},
{header:"性别",dataIndex:"SEX"},
{header:"登录帐号",dataIndex:"LOGIN_NAME"},
{header:"创建人",dataIndex:"CREATE_USER_NAME"},
{header:"创建时间",dataIndex:"CREATE_TIME"},
{header:"最后修改时间",dataIndex:"UPDATE_TIME"},
{header:"邮箱",dataIndex:"EMAIL"},
{header:"联系方式",dataIndex:"PHONE"},
{header:"状态",dataIndex:"ACTIVE"},
{header:"上次登录IP",dataIndex:"IP"},
{header:"上次登录时间",dataIndex:"LAST_TIME"}
];
this.tbar=[//初始化工具栏
"->",
{text:"新增",action:"add",iconCls:"Add"},
{text:"删除",action:"delete",iconCls:"Delete"},
{text:"编辑",action:"edit",iconCls:"Edit"},
{text:"查看岗位",action:"searchpost",iconCls:"Edit"},
{text:"导出",action:"export",iconCls:"ExportExcel"},
{text:"筛选",action:"search",iconCls:"Search"}
];
this.putStore("sys.user.store.UserListStore");
this.callParent(arguments);
}
});
alias:为类定义别名。
constructor:构造函数,构造函数的作用不解释了,和java一样。
this.callParent(arguments):调用父类构造函数,Extjs中规定:在继承关系中,子类有构造函数,不会自动调用父类构造函数,如果需要执行父类构造函数,需要手动调用。
C:也是Extjs的核心,逻辑处理、交互。
Ext.define("sys.user.controller.UserController",{
extend:"Ext.base.controller.BaseController",
models:["Ext.base.model.BaseModel"],
editUrl:window.ROOT+"/sys/user/edituser",//编辑用户
init:function()
{
var me=this;
var grid;
var winForm=null;
var postWin=null;
var searchPostWin=null;
this.control({
"UserView":{
afterrender:function(gp){
grid=gp;
winForm=null;
postWin=null;
grid.down("button[action=add]").on("click",addUser);
grid.down("button[action=delete]").on("click",deleteUser);
grid.down("button[action=edit]").on("click",updateUser);
}
}
});
/**
* 修改用户
*/
function updateUser(){
var rows=grid.baseIsSelected();
if(rows){
if(winForm==null)
{
winForm=Ext.create("sys.user.view.UserFormView");
winForm.down("button[action=save]").on("click",editUser);
}
var form=winForm.down("form");
form.reset();
winForm.setTitle("修改信息");
form.down("hidden[name=OPER]").setValue("edit");
form.getForm().loadRecord(rows[0]);
winForm.show();
}
}
/**
* 添加用户
*/
function addUser(){
if(winForm==null)
{
winForm=Ext.create("sys.user.view.UserFormView");
winForm.down("button[action=save]").on("click",editUser);
}
winForm.down("form").reset();
winForm.setTitle("添加信息");
winForm.down("hidden[name=OPER]").setValue("add");
winForm.show();
}
/**
* 删除用户
*/
function deleteUser(){
grid.baseDeleteRow(me.editUrl);
}
},
/**
* push to panel
* @return {}
*/
show:function(){
return Ext.create("sys.user.view.UserView");
}
});
init:初始化函数。
this.control:控制器控制核心。
MVC有了,那么数据怎么访问呢,怎么请求后台呢?
补充一个Store,类似java中的数据访问层Dao
Ext.define("sys.user.store.UserListStore",{
extend:"Ext.base.store.BaseGridStore",
proxy:{type:"ajax",url:window.ROOT+"/sys/user/getuserlist",reader:{rootProperty:"data",totalProperty:"result"}},
model:"sys.user.model.UserModel"
});
目录结构如下:
这样一来,前端的代码也被我们用MVC的模式分离出来了。为什么这么设计呢?本来一个js就搞定的事情,为什么要分好几个呢?我在刚接触Extjs4.2的时候也很困惑。后来在项目中体会到了它的好处,当前端的交互很多的时候,交互很复杂的时候,有了这样的分层,代码维护起来轻松多了。
上面写的这些,只是对Extjs简单的认识,网上还有很多更详细的教程,这里不在罗说。
笔者是个写后台的程序员,在平时的学习中,少不了自己研究写新鲜东西,有了后台代码,就要有前端来展示,笔者又是个强迫症受害者,不好看的界面绝对不用,CSS功底不足,于是乎我和Extjs相识了。
笔者平时喜欢封装一些公共类来满足项目中的使用,让别人调用自己的代码,那感觉。。。。。。程序员们都懂!所以老毛病又犯了,Extjs中我也进行了二次封装,把一些常用的组件分装起来,下面会附上代码。
上面的代码中大家可能看到了有些其实就是我已经分装过的,比如:
extend:"Ext.base.model.BaseModel",//BaseModel就是分装的一个“类”
this.putStore("sys.user.store.UserListStore");//putStore就是分装的函数
二次分装的好处:假如有一天Extjs升级了,7.0,8.0,10.0,那么我们之前的项目想升级怎么办,如果过于依赖Extjs原有的封装,那么我们就太被动了,升级一次,大改一次。但是我们对Extjs的常用功能进行二次封装,用我们自己的函数实现Extjs的函数,那么升级就变得简单了,我们在升级的时候只需要升级二次封装的代码就OK了。
下面的目录结构是笔者亲自封装的:
业务中的Controller都继承BaseControlle,Model继承BaseModel,Store继承对应的Store,View继承对应的View。
下面是我对GridPanel的简单分装:
Ext.define("Ext.base.view.BaseGridView",{
alias:"widget.BaseGridView",
extend:"Ext.grid.Panel",
singleton:false,//指定为true类定义单例类(该类只能有一个实例)
region:"center",
columnLines:true,
forceFit:true,
border:false,
page:true,
searchFiled:true,
selModel:{selType:"checkboxmodel"},//复选框
viewConfig:{
markDirty:false,
enableTextSelection:true
},
columns:[],
putStore:function(s){//自定义函数
var st=Ext.create(s,{proxy:{extraParams:{"p":this.page}}});
this.store=st;
if(this.searchFiled){//搜索文本框
if(!this.searchFiledName){
searchFiledName="";
}
if(!this.tbar){
this.tbar=[];
}
var txSearchField=Ext.create("Ext.plug.form.SearchField",{width:300,emptyText:this.searchFiledName,labelWidth:0,store:st});
this.tbar.unshift(txSearchField);
}
if(this.page){
this.bbar=[{xtype:"pagingtoolbar",store:st,displayInfo:true}];
}
for(var i=0;i<this.columns.length;i++){
if(!this.columns[i]["align"])this.columns[i]["align"]="center";
if(!this.columns[i]["width"])this.columns[i]["width"]=150;
}
},
/**
* 获取选中行
*/
baseIsSelected:function(){//自定义函数
var list=this.getSelectionModel().getSelection();//Extjs的函数
if(list.length>0){
return list;
}
else {
return false;
}
},
/**
* 数据刷新
*/
baseReloadData:function(){//自定义函数
this.getStore().loadPage(1);//Extjs的函数
}
});
学习Extjs的过程中,我在这里总结下:
阶段1:读懂一个demo代码,理解设计模式和编程是想。
阶段2:理解事件、函数,绑定事件、触发函数。(afterrender,beforeload等)这些经常用的关键词要深刻体会。
阶段3:本章介绍的Extjs不是很多,因为笔者希望和大家分享编程思想,而不是Extjs的技术点。技术点有很多博客都已经写的很详细了,官网也有API。在Spring中经常会提到AOP,笔者也是受AOP思想的启发,在程序员之路上,慢慢摸索着。