论坛首页 Web前端技术论坛

GWT项目和开发总结

浏览 9268 次
精华帖 (0) :: 良好帖 (13) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-09-29   最后修改:2009-10-04
     从2007.08进入项目到2008.11产品正式运营,经历了两个GWT项目开发(这两个项目共享相同widget)。基本全程都很顺利。现在分享一下开发历程和注意事项。
     开发环境: ubuntu7.10 + apache php + jboss.
     下面是开发总结:

1: GWT Version & Upgrade
得到一份(新)版本后,你得作如下的工作:
1)看看支持哪些功能和特性
2)看看修正了哪些bug
3)看看屏蔽了哪些function
4)更新相关的编译,运行脚本
      这通常是很有用的。比如, 一开始就知道GWT支持国际化这是非常重要的。还有就是
需要知道支持哪些java package, classes, 以及jdk版本和语法特性。这比编译通不过,再来反复修改,要节省大量时间。另外,版本的升级,部分代码需要跟着同步。 比如,
GWT 1.4.2 如果想让一个Popup在  browser居中,得自己另外写方法。 GWT 1.4.6就提供
popupPanel.center()直接支持。 GWT1.6.9时float类型已经不被支持,需要进行相应替换才能编译通过。

2:系统配置项:
1)XML Schema(怎么取得这些数据,参考下面的Ajax设计模式)
2)Http header. 比如: 
<head>
<!-- HB, 2008/09/09 -->
		<meta id="require-logged-in" name="required" content="false"/>
</head>

取得这些数据的方法:
public static String getMetaContentValueById(String metaTagId,String defaultValue){
		String result = null;
		try{
			Element elem = DOM.getElementById(metaTagId);	
			result = DOM.getElementAttribute(elem, CommonDefn.LOCALE_TOKEN);
			result = (result == null) ? defaultValue : result;
			result = (result.trim().length() < 1) ? defaultValue : result;
		}
		catch(Exception e) {
			result = defaultValue;
		}
		return result;
	}

3:完全组件化开发
自认为分模块(module)开发是不必要的,而且编译的开销特别大。
我们主要的分页面(Page)架构是基于History(Listener)的。从一个页面到另一个页面的代码页很简单: History.newItem(_link); GoBack也很简单:   History.back();
这样的架构的一个额外好处,还解决了Ajax的go back问题。 我们可以判断一个页面是通过正常的路径来的,还是通过go back来的。 这样就可以分别对待处理。


4:JSNI & JavaScript
1) 直接写javascript function,供页面或flash 调用
2)使用JSNI作多语言(英文,繁体,简体) 的选择
 protected class HyperlinkCommand implements Command {
        protected String _url = "";
        public HyperlinkCommand(String url) {
            _url = url;
        }

        /**
         * Redirects the browser to the given URL.
         * The following is an inline javascript:
         * 	$wnd is the handler for the browser
         *  .location.href is the relative url
         */
        private native void link(String url) /*-{
            $wnd.location.href = url;
        }-*/;

        public void execute() {
        	link(_url + CommonDefn.HTML_PAGE_SEPARATOR + History.getToken());	// the language link follows by the original page
        }
    }

5:架构属于你自己的RPC  & Servlet.
方便及时修改,测试(在eclipse里运行测试或debug),发布(考虑css兼容问题,你得在自己的browser测试).

Service部分: 采用一个Servlet来作集中控制。前端发送两个参数到Server, I.E.
/proxy/proxy.php?proxyinstr=getavailmovies&jsondto={"uusystemlanguageid":"1", "uuxxx":"1"}


Main.gwt.xml的
<servlet path='/proxy/proxy.php' class='xxxx.debug.proxy.HttpProxy'/>

注意上面的class部分,他不在client包中,这也是我们想要的效果。 这个只供测试RPC用.
而httpProxy,是标准的Servlet.
public class HttpProxy extends HttpServlet {
	public void doGet(HttpServletRequest httpRequest, HttpServletResponse httpResponse) 
	throws ServletException, IOException {
		doPost(httpRequest, httpResponse);
	}
	/**
	 * Process POST requests
	 */
	public void doPost (HttpServletRequest httpRequest, HttpServletResponse httpResponse)
	throws ServletException, IOException {
       ...
}


}

之所以把这个包命名为debug,只是为了在eclipse里运行的时候debug rpc. 它是不会被编译输出到www的。

  当在Browser(Firefox,以后简称FF)运行的时候,实际是访问Apache的php,而不是自己建立的Servlet(它负责把数据直接转发到8080的jboss). 而proxy.php也正是作这件事,把80端口的请求转发给8080的jboss的一个Servlet.这就是架构的巧妙之处。

   技巧: 在apache下建立soft link到 开发环境下的目录(编译后生成的),就少了N次的cp to apache .


6:Server与Client的约定(term)
1)共享一份相同的常量,定义在各自的CommonDefn中。 i.e.
public interface CommonDefn {
	public static final int 		PERMISSIONS_CHECKOUT			=	0x02000000;
 }
如果一方有修改,另一方需要同步。
2)由于双方都是用json交换数据,所以对于每个请求,它的json要稳定。如果Server端准备
返回的json有变化,则要同步client。一般数据库的设计发生变化,则要修改Server端。 Server的变化,则带来Client的同步变化。因此,数据库设计决定了整个project的可维护性。
3)Client 与 Server共同维护 (自定义的) Session
4)在Client的CallbackHandler中,如果收到"Session expired!"的 json message, 则需要要求用户(重)登录
5)Client做数据的sort,cache还是Server做? 最后还是Server做。唯一原因是:考虑GWT最终输出是html, js. 精简client始终是整个开发流程都要遵守的规则。


7:Ajax设计模式
1)读取本站的XML
HTTPRequest.asyncGet(_xmlFile, new ResponseTextHandler() {
			      public void onCompletion(String responseText) {
			        // In the real world, this text would come as a RPC response. This
			        // technique is great for testing and samples though!
			    	  renderXML(responseText);
			      }

2)一次发送多条请求(以后称指令),待所有请求完成之后,再作处理。这样能减少指令间的耦合度。
处理方式,每一条指令返回后,在callBack中把计数加1,当所有的计数达到指令的条数后,再作具体的处理。

3)go back问题。我们用public class HistoryStack extends Stack {}的一个
SingleTon的实例来记录页面的流入,流出。来达到HTML Referrer的效果。 也就是说,能够
知道当前页面是来自于哪个页面,在此基础上,再判断是否是goback到达的。然后再作处理

4)TimeOut问题。 网络的延迟,中断,甚至处理时间超过指定的等待时间,都可能引发TimeOut问题。如果你开发的是嵌入式系统。这个问题,就得慎重考虑。目前我们的处理方式:客户端触发的TimeOut处理,我们转入到一个固定页面,这个页面显示:系统正在维护中。 这个页面会定时连接server,当连接成功,重置到index page.  另外,服务端与客户端维持一个
heartbeat,如果检查到一定时间没有(heartbeat),就重新启动client.


8:GWT & 动画效果 以及 Flash
1)用GWT开发动画效果,是很容易的。记住,GWT既面向Widget,也面向DOM.
2)Flash与GWT通讯这里不多说 . 在linux下的FF, Flash可能会跳出来,挡住Popup, alert,menu 也就是说transparent特性无效。如果含有的Flash较多,这简直就是灾难。 我们采取的做法,以Flash挡住Popup为例子,我们采取FlashAnimation来显示Flash,FlashPopupManager来管理Flash与Popup的关系。以两个Popup(上面都有Flash)先后显示为例子.第2个Popup(上层的)显示的时候,把第一个Popup的Flash隐藏。第2个Popup关闭的时候,再显示第一层的flash.这是workaround.然而很有效。 算法采用Stack.


9:异常处理
1)GWT提供全局的异常处理(也就是说能够捕获未被处理的异常), I.E. 
// set uncaught exception handler, we should log exception for investigation
	    GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
	    	public void onUncaughtException(Throwable e) {
	    		// TODO
	    		// should call instruction for error message logging
	    		e.printStackTrace();
	    		Window.alert("Uncaught exception:\n" + e);
	    		
//	    		String message = "";
//	    		message += "GWT.getHostPageBaseURL(): " + GWT.getHostPageBaseURL();
//	    		message += "GWT.getModuleBaseURL(): " + GWT.getModuleBaseURL();
//	    		message += "GWT.getModuleName(): " + GWT.getModuleName();
//	    		message += "GWT.isScript(): " + GWT.isScript();
//	    		Window.alert("GWT message:\n" + message);
	    	}
	    });


       2)局部处理,同java一样: try, catch, finally block.

10:加入第三方module(通常也是直接或间接由GWT开发)
1)注意版本的匹配和同步升级
2)在project.gwt.xml中加入模块 . I.E
. <inherits name='com.allen_sauer.gwt.voices.gwt-voices' />

3)在编译和运行的脚本中加入这些library的path

11:多个Project(在同一个目录下)共享widget(不同于上面说的引入module方式)
1)设置各自的project.gwt.xml,以及配置里面的servlet path
2)在client Package下建立各自的EntryPoint Class
3)在public Folder下建立各自的Host web File
4)建立各自的编译(project-compile),运行(project-shell)脚本

   发表时间:2009-10-09  
谢谢,很受启发
0 请登录后投票
   发表时间:2009-10-09  
楼主的意思GWT只是做了M的工作,V的工作由PHP代替了,我理解的对么?
0 请登录后投票
   发表时间:2009-10-09  
喜欢GWT的朋友,推荐两本好书:  《GWT in Practice》(Robert Cooper and Charles Collins), 《GWT In Action》(Robert Hanson)。
0 请登录后投票
   发表时间:2009-10-09  
rockman 写道
楼主的意思GWT只是做了M的工作,V的工作由PHP代替了,我理解的对么?

GWT都作"V"的工作。V可以是一个简单index.html,也可以是index.php或index.jsp.  与一般的开发不同的是,只要这么一个
Host Web file(index.html or index.jsp)就可以了,亦即: One Page One Application.
0 请登录后投票
   发表时间:2009-10-09  
可是我们编译后的V非常大,一个模块需要2M左右,在广域网下有性能问题,特别是Open Page Mutiple Application
0 请登录后投票
   发表时间:2009-10-09  
还有,不太清楚你们是怎么把业务对象反映在V上的,我们通常的做法是有一个M to V
的过程。从V到M又经历了从V to M的过程,这部分的代码都需要编译到client包中,所以导致V非常的大。请教你们是怎么处理,不胜感激!
0 请登录后投票
   发表时间:2009-10-09  
rockman 写道
可是我们编译后的V非常大,一个模块需要2M左右,在广域网下有性能问题,特别是Open Page Mutiple Application


或许,你可以尝试只编译输出给主流的browser(ie && ff).
ps: 还没有做过分Module开发,也暂时没有关注性能调优.
0 请登录后投票
   发表时间:2009-10-09   最后修改:2009-10-09
rockman 写道
还有,不太清楚你们是怎么把业务对象反映在V上的,我们通常的做法是有一个M to V
的过程。从V到M又经历了从V to M的过程,这部分的代码都需要编译到client包中,所以导致V非常的大。请教你们是怎么处理,不胜感激!


如果这个M用到的地方非常之多,就使用。否则,不用M. 我们大多数,直接 JSON --> V.
0 请登录后投票
   发表时间:2009-10-09  
已经是对各个浏览器编译了,还是大,性能调优愁死我了,想方设法缩小前台编译大小啊。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics