很多希望学习或需要学习Ext JS的开发人员,都会习惯性地问以下问题:Ext JS与自己熟悉的开发语言结合得如何?开发起来方便吗?对于初次接触Ext JS的人来说,问这个问题不奇怪,毕竟Ext JS与他们之前所熟悉的开发模式有很大不同。如果对Ext JS了解深入并掌握开发方法后,就会发现,Ext JS其实是一个很好的跨语言平台的开发框架。对于使用混合平台或需要进行平台迁移的项目来说,是非常好的选择。在本章,在介绍Ext JS的跨平台特性之外,还会通过把简单的CMS系统迁移到Java平台的演示来验证Ext JS的跨平台特性。
13.1 Ext JS跨平台特性简介
这里所说的Ext JS跨平台特性主要是指Ext JS的开发语言平台无关性,也就是说,无论你熟悉的后台开发语言是C#、Java还是PHP,甚至是C++或C,都没有关系,只要能根据固定的格式返回数据,使用Ext JS开发的应用程序就能正常运行。进一步来说,Ext JS的开发工作,可以由前端工程师使用模拟数据独立完成,而无须知道后台是如何运作的。
为什么Ext JS会有这样的跨平台特性呢?这主要是缘于Ext JS通过存储等组件实现了数据与界面的分离,以及通过Ext.Ajax实现了数据交互接口的标准化。
使用Ext JS,最大的感受就是数据与界面是分离的,数据的操作基本都可以围绕存储进行,而存储与后台之间的数据交互则使用标准化的格式进行。对于不一定与存储打交道的表单等数据的交互,也是以标准化格式进行的。这些因素综合起来,就彻底把后台与前台界面分离开来了。这样做的最终结果,就把Ext JS打造成了可以实现跨平台的框架。
这样说有点抽象,还是做个演示,使大家对Ext JS的跨平台特性有个清晰的认识。
13.2 在JAVA平台使用SimpleCMS
13.2.1 概述
要验证Ext JS是否能实现跨平台的迁移,只需要把简单的CMS系统的应用程序直接复制到Java平台上,看是否需要脚本代码就清楚了。如果不需要,就说明Ext JS是可以实现跨平台迁移的,如果需要修改,则说明不可以。
13.2.2 搭建开发环境
本书使用的Eclipse版本是4.7.0版。打开Eclipse,并创建一个名为SimpleCMS-JAVA的动态网页项目。项目创建后,先别急于把应用程序添加到项目里,否则会因脚本验证造成假死。
要取消脚本验证,选择主菜单Window→Preferences打开Preferences对话框。在对话框左侧找到Validation,然后在右边的列表中找到Client-side JavaScript,把Manual和Build两列处于选中状态的复选框取消选择。最后单击Apply and Close完成更改。
13.2.3 添加应用程序
把验证取消后,就可以把应用程序复制到项目的WebContent\Sencha文件夹里了。
13.2.4 创建首页文件
在项目的WebContent文件夹下新建一个index.jsp文件,然后把WebContent\Sencha文件夹下的index.html文件里的内容替换掉index.jsp中的HTML代码。
代码复制完成后,在加载bootstrap.js文件的SCRIPT标记上添加以下ROOTPATH的定义代码:
<script>
var ROOTPATH = 'http://localhost:8080/SimpleCMS-JAVA';
</script>
ROOTPATH添加完后,为bootstrap.js添加路径Sencha,注意大小写。
13.2.5 修改app.json文件
打开app.json文件,将indexHtmlPath中的index.html修改为index.jsp。然后生成一次应用程序。
13.2.6 修改SimpleCMS.util.Url
打开SimpleCMS.util.Url的类文件,把getResource方法中的sencha修改为Sencha。Tomcat默认配置下路径区分大小写,比较麻烦。
13.2.7 添加Json-lib
对于一般的Ext JS项目来说,返回JSON格式是必不可少的,所以必须为项目添加JSON库,这里将使用Json-lib这个库,可以到http://json-lib.sourceforge.net/中下载。
要使用这个库,还需要以下类库支持:
- jakarta commons-lang 2.5
- jakarta commons-beanutils 1.8.0
- jakarta commons-collections 3.2.1
- jakarta commons-logging 1.1.1
- ezmorph 1.0.6
对于以上几个公共库,可以访问http://commons.apache.org/来下载。对于ezmorph库则可以访问http://ezmorph.sourceforge.net/来下载。
13.2.8 辅助类ExtJs
在6.2.3小节,创建了辅助类ExtJS,并添加了一个WriterJObject方法来统一输出接口,而这正是实现数据标准化交互的关键之一,在Java项目中,这当然也是少不了的。
下面,在项目中新建一个Java类,并加入静态方法WriterJObject,代码如下:
package Helper;
import net.sf.json.*;
public class ExtJs {
public static JSONObject WriteObject(
boolean success,
JSONObject errors,
int total,
String msg,
JSONObject dataJsonObject,
JSONArray dataJsonArray
) {
JSONObject jo = new JSONObject();
jo.put("success", success);
if(errors != null){
jo.put("errors", errors);
}
if(total>0){
jo.put("total",total);
}
if(msg != null){
jo.put("msg", msg);
}
if(dataJsonObject != null){
jo.put("data", dataJsonObject);
}
if(dataJsonArray != null){
jo.put("data", dataJsonArray);
}
return jo;
}
}
从代码可以看到,把类放在了Helper包里。由于不清楚Java如何定义可变参数,也不知道能否将data参数定义为object类型,然后自行转换为JSONObject或JSONArray进行处理,所以把全部参数都定义为固定参数,且定义了dataJsonObject和dataJsonArray这两个参数来处理JSONObject和JSONArray类型的data属性。
与C#版的还有个不同的地方是total参数,由于不能为null,且该参数只有在值大于0的时候才有意义,所以,将它设置为大于0的时候才将total属性添加到返回对象中。
有了这个方法,一切就变得简单了。
13.2.9 创建Servlet:GetUserInfo
现在,一切都准备好了,可以进行测试。在项目中新建一个名为UserInfo的Servlet,包的名称为Account,访问地址为account/userinfo。文件创建后,先添加getMenu方法用来返回菜单,代码如下:
protected JSONArray getMenu() {
JSONObject menu1 = new JSONObject();
menu1.put("text", "文章管理");
menu1.put("iconCls", "x-fa fa-file-text-o");
menu1.put("rowCls", "nav-tree-badge");
menu1.put("viewType", "articleView");
menu1.put("routeId", "articleView");
menu1.put("leaf", "true");
JSONObject menu2 = new JSONObject();
menu2.put("text", "媒体管理");
menu2.put("iconCls", "x-fa fa-file-image-o");
menu2.put("rowCls", "nav-tree-badge");
menu2.put("viewType", "mediaView");
menu2.put("routeId", "mediaView");
menu2.put("leaf", "true");
JSONObject menu3 = new JSONObject();
menu3.put("text", "用户管理");
menu3.put("iconCls", "x-fa fa-user");
menu3.put("rowCls", "nav-tree-badge");
menu3.put("viewType", "userView");
menu2.put("routeId", "userView");
menu3.put("leaf", "true");
JSONArray jArray = new JSONArray();
jArray.add(menu1);
jArray.add(menu2);
jArray.add(menu3);
return jArray;
}
如果有数据库,可以从数据库中获取菜单。菜单写好以后,在doGet方法中添加以下代码来返回用户信息:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
JSONObject userInfo = new JSONObject();
userInfo.put("UserName", "admin");
userInfo.put("Roles", "['系统管理员']");
JSONObject jo = new JSONObject();
jo.put("UserInfo", userInfo);
jo.put("Menu", getMenu());
JSONObject result = ExtJs.WriteObject(true, null, 0, null, jo, null);
response.setContentType("text/javascript; charset=utf-8");
response.getWriter().write(result.toString());
}
在doGet方法中,只是参照C#代码中登录后的返回结果组合了一些用户信息返回客户端。
现在可以运行项目了。项目运行后,会发现页面已经顺利地进入到管理系统,显示与在C#平台上看到是一模一样的。这说明,迁移已经成功了。余下的工作基本就是根据所需的数据定义SerLet进行数据交互了。至于客户端的代码,基本不需要做任何修改,是不是很方便?
13.3 在PHP平台使用SimpleCMS
13.3.1 搭建开发环境
本书将使用Yii框架 的基本模版来实现PHP版本的SimpleCMS。在地址https://github.com/yiisoft/yii2/releases/download/2.0.12/yii-basic-app-2.0.12.tgz下载完框架包后,先使用管理员权限运行解压缩软件,然后再打开框架包,将包里的basic目录解压出来。需要使用管理器权限是因为有些文件不用管理员权限解压不出来,比较麻烦。
文件解压后,把basic里的文件全部复制到项目的文件夹,笔者的项目文件夹为D:\Workspace\PHP\SimpleCMS-PHP。
13.3.2 在XAMPP设置访问路径
这个Yii2框架总体来说还算不错,但最让人讨厌的就是它的访问路径设置。最简单的设置就是给一个虚拟域名给它,不要搞什么虚拟目录之类的。如果使用高级模版,那更烦人。
在XAMPP的控制面板(Control Panle)中,在Apache模块的Config的下拉列表中选择Apache(httpd-xampp.conf),打开Apache的xampp配置文件。然后在配置文件添加以下代码替换XAMPP原来的根目录设置:
DocumentRoot "/Workspace/PHP/SimpleCMS-PHP/web/"
<Directory "/Workspace/PHP/SimpleCMS-PHP/web/">
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php
Options Indexes FollowSymLinks Includes ExecCGI
AllowOverride All
Require all granted
</Directory>
配置文件保存后,重新启动一下Apache。
现在访问网站,会出现Invalid Configuration的错误,因为还没配置好Yii框架。
13.3.3 配置Yii框架
打开项目的config文件夹下的web.php文件,找到cookieValidationKey配置,将它的值修改为simplecms,然后找到urlManager配置,将它的代码修改为以下代码:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'baseUrl' => '/',
'rules' => [
],
],
现在再打开网站,就可以顺利看到示例页面了。
13.3.4 添加应用程序
如果使用的是Eclipse作为PHP的开发工具,请参展13.2.2节的内容,先将脚本验证取消,再将应用程序复制到项目的web\Sencha文件夹下。
应用程序复制后,将web\Sencha文件夹下index.html文件复制到web文件夹下作为伪装的首页文件。
13.3.5 修改首页文件
打开views\site文件夹下的index.php文件。先将title变量删除,然后使用index.html内的代码覆盖index.php内的HTML代码。
代码复制后,在加载bootstrap.js的SCRIPT标记上,添加以下ROOTPATH的定义代码:
<script type="text/javascript">
var ROOTPATH = 'http://localhost:8081';
</script>
为了避免和IIS的冲突,笔者将XAMPP的访问端口修改为了8081,具体使用什么端口,请大家根据自己的XAMPP设置咨询修改。
添加ROOTPATH后,将bootstrap.js的路径修改为sencha。
由于Yii框架的页面默认是使用布局的,但这个布局对于我们来说完全是多余,因而,我们必须修改控制器,让它不渲染布局。打开controllers文件夹下的SiteController.php文件,找到actionIndex方法,将渲染首页使用的render方法修改为不使用布局的renderAjax方法。
13.3.6 辅助类ExtJs
由于PHP输出JOSN数据比较方便,有没有6.2.3小节所讲述的辅助类ExtJs其实问题不大,不过,辅助类的其他方法还是需要的。
要创建辅助类ExtJs,可以在项目中新建一个名为common的文件夹,然后在文件夹内新建一个名为ExtJs的类。类创建后,在类内添加一个名为WriteObject的静态方法,代码如下:
public static function WriteObject($success, $errors =null, $total= null, $msg=null, $data=null)
{
$obj= array ('success'=>$success);
if(!empty($errors)) $obj['errors'] = $errors;
if(!empty($msg)) $obj['msg'] = $msg;
if(!empty($total)) $obj['total'] = $total;
if(!empty($data)) $obj['data'] = $data;
return json_encode($obj,JSON_UNESCAPED_UNICODE);
}
在类中,要保证命名空间为app\common。
13.3.7 创建AccountController控制器
在controllers文件夹下新建一个名为AccountController的类,类的命名空间为app\controllers。类创建后,参考SiteController,先把use开头的代码复制过来,然后在类声明上添加extends关键字,让AccountController派生于yii\web\Controller。
接下来添加getMenu方法,代码如下:
private function getMenu(){
return array(
0=>array( 'text'=>'文章管理', 'iconCls'=>'x-fa fa-file-text-o', 'rowCls'=>'nav-tree-badge', 'viewType'=>'articleView', 'routeId'=>'articleView', 'leaf'=>'true'),
1=>array( 'text'=>'媒体管理', 'iconCls'=>'x-fa fa-file-image-o', 'rowCls'=>'nav-tree-badge', 'viewType'=>'mediaView', 'routeId'=>'mediaView', 'leaf'=>'true'),
2=>array( 'text'=>'用户管理', 'iconCls'=>'x-fa fa-user', 'rowCls'=>'nav-tree-badge', 'viewType'=>'userView', 'routeId'=>'userView', 'leaf'=>'true')
);
}
代码就是一些数组的简单组合,写起来比Java那个方便多了。
最后是添加actionUserinfo方法,代码如下:
public function actionUserinfo()
{
return ExtJs::WriteObject(true,null,null,null,array(
'UserInfo'=> array('UserName'=>'admin', 'Roles'=>'系统管理员'),
'Menu'=> $this->getMenu()
));
}
在Yii框架中,控制器中的操作都是以action开头的方法,因而方法名必须是actionUserinfo。在actionUserinfo方法,只是参照C#代码中登录后的返回结果组合了一些用户信息返回客户端。
现在浏览器打开项目,会发现页面已经顺利地进入到管理系统,显示与在C#平台上看到是一模一样的。这说明,迁移已经成功了。余下的工作基本就是根据所需的数据定义控制器进行数据交互了。至于客户端的代码,基本不需要做任何修改,是不是很方便?
13.4 小结
在本章,主要讲述并演示了Ext JS的跨平台性。很多Ext JS的初学者,都喜欢找与自己所熟悉的开发语言的Ext JS示例来学习,而且总是会问一些使用某种语言如何返回数据和获取数据的问题。这说明这位初学者在学习思路上已经走偏了,而结果可能是越学越难,最终不得不放弃。
要实现Ext JS的跨平台性,一个很关键的地方还是要看开发人员的开发方式,如果用老思维来开发Ext JS的应用程序,在页面中混合好多HTML代码和Ext JS脚本,那么也是实现不了Ext JS的跨平台性的。而这样也就无法完全发挥Ext JS的优点。
本章的目的就是做个范例,让大家来学习如何充分利用Ext JS的特性来进行开发,以便开发出健康的、可维护性高和代码可阅读性高的应用程序。