61. 两种类型的web框架: 基于请求的和基于组件的
概述
现在的web框架可以分为基于请求的(request-based)和基于组件的(component-based)两大阵营。前者的代表有Struts和Spring MVC等,后者的成员则有JSF、Tapestry、ASP.NET等等。
基于请求的框架较早出现,它用以描述一个web应用程序结构的概念和传统的静态Internet站点一样,是将其机制扩展到动态内容的延伸。对一个提供HTML和图片等静态内容的网站,网络另一端的浏览器发出以URI形式指定的资源的请求,Web服务器解读请求,检查该资源是否存在于本地,如果是则返回该静态内容,否则通知浏览器没有找到。Web应用升级到动态内容领域后,这个模型只需要做一点修改。那就是web服务器收到一个URL请求(相较于静态情况下的资源,动态情况下更接近于对一种服务的请求和调用)后,判断该请求的类型,如果是静态资源,则照上面所述处理;如果是动态内容,则通过某种机制(CGI、调用常驻内存的模块、递送给另一个进程如Java容器)运行该动态内容对应的程序,最后由程序给出响应,返回浏览器。在这样一个直接与web底层机制交流的模型中,服务器端程序要收集客户端籍get或post方式提交的数据,转换,校验,然后以这些数据作为输入运行业务逻辑后生成动态的内容(包括HTML、JavaScript、CSS、图片等)。
基于组件的框架采取了另一种思路,它把长久以来软件开发应用的组件思想引入到web开发。服务器返回的原本文档形式的网页被视为由一个个可独立工作、重复使用的组件构成。每个组件都能接受用户的输入,负责自己的显示。上面提到的服务器端程序所做的数据收集、转换、校验的工作都被下放给各个组件。现代web框架基本上都采用了模型、视图、控制器相分离的MVC架构,基于请求和基于组件两种类型大都会有一个控制器将用户的请求分派给负责业务逻辑的模型,运算的结果再以某个视图表现出来,所以两大分类框架的区别主要在视图部分,基于请求的框架仍然把视图也就是网页看作是一个文档整体,程序员要用HTML、Javascript和CSS这些底层的代码来写“文档”,而基于组件的框架则把视图看作由积木一样的构件拼成,积木的显示不用程序员操心(当然它们也是由另一些程序员开发出来的),只要设置好它绑定的数据和调整它的属性,把他们大大从编写HTML、Javascript和CSS这些界面的工作中解放出来。
优劣比较至此,两种类型的框架似乎已分出优劣——基于组件的框架封装了基于请求的框架在视图方面的细节,省去了程序员的精力和时间,是一种进步。实际情况却并非这么简单。在传统的桌面应用程序的开发中,类似的论断没有任何问题,基于界面组件(在这个上下文里就是控件)的技术早已成为标准,Visual Studio系列、Dephi、PowerBuilder等等都是如此,没有人会想去用底层的GUI接口自己去画一个个窗体、菜单、树目录……
但是web应用的界面开发却有很大不同。首先它是基于HTML这样的描述型(declarative)语言,几行文本就可以“画”出包括表格、按钮的一个简单页面,在构建界面上比传统的桌面开发的命令型(imperative)语言高效。如果是使用现成的元件(如文本框、组合框)构建界面,前者比后者用的行数要少。如果是创建新的控件(如菜单、树目录、导航器),前者利用DIV、TABLE、IMG之类的基本材料,CSS赋予的丰富显示效果和Javascript带来的动态行为也比用C++、Java这样的语言新开发一个控件容易。基于请求的框架与基于组件的框架在开发视图的工作量和难度上的差别就没有传统的桌面应用程序里自己动手开发控件和使用现成的控件的差别那么大。
另一方面,浏览器是迄今为止最难为其开发和调试程序的环境。HTML最初是被设计用来显示静态的信息,对它解析的高度容错性是为了让浏览器尽量能显示文档的内容,忽略粗心的编写者犯的错误。这样做的副作用就是纵容错误代码的普遍存在。当HTML被用来设计应用程序的界面时,这种容错的风格就不适合了。HTML的编辑器普遍允许语法错误的存在,浏览器载入时只是会显示异常,不会明确地报告错误,这样就给发现潜在的问题和问题发生时定位来源带来很大困难。类似的,Javascript被发明时宽容的解释运行风格也是因为它被定位为执行一些辅助性的功能,同时为了减少对程序员的严格性上的要求。当Javascript成为web应用程序必不可少的前端开发语言时,这种宽松性也导致了很大的代价。同时由于web应用程序的流行和各大厂商的重视,浏览器作为前端的平台软件也告别了一家垄断,呈现群雄逐鹿的局面。不同浏览器在标准执行和功能扩展上的差别又给开发人员带来新的挑战——如何使网站能在所有主流浏览器上正常显示和操作?最后,由竞争带来的HTML、Javascript、CSS等前端技术和编程模式的进化和浏览器升级的日新月异使得程序员有必要及时更新代码——一个出色的web应用程序几个月后就可能因为用户使用另一种或升级后的浏览器显示或操作异常。也就是说,应用程序的视图的寿命与传统的桌面应用程序相比缩短了,即使不是为了美观而重新设计也需要为了保持可用性维护和修改。
有了上述两方面的讨论,我们再来比较基于请求(以Java语言为例)和基于组件两种框架。在概述一节里,已经提到两者的差异主要是在视图技术上。前者的视图是由代码组成的连续体,HTML展示网页的静态部分,嵌入的Java计算动态的内容。一个JSP页面本质上和一个HTML页面是相同的,如果说浏览器接收到的HTML页面是最终的成品,JSP页面则是待加工的半成品。在近年来随Ajax出现的极端情况下,页面完全由HTML、CSS和Javascript构成,动态的部分依靠Ajax获取服务器返回的数据后依靠Javascript拼装而成。后者则将视图看作多个边界清晰的组件嵌套而成的树结构,是对HTML页面的包装和抽象,页面某一段HTML只是对应组件的特定方法(不妨称之为render)输出的字符串。正是由于多了这一层包装,在render方法里编写HTML、CSS和Javascript比在一个文本文件里编写更复杂冗长。对于一次性编写,反复使用的组件,这本不是大问题。麻烦在于前面说过作为最终产品的前端代码因为种种原因是频繁需要更改和升级的,一个Visual Basic的控件可以稳定使用很多年,一个JSF的服务器端组件则需要靠打修复包和版本升级来延长生命。改动一个组件自然比修改一个文本文件成本高。至此只是站在组件开发者的角度,对于普通的web开发人员,本来只需要了解简单组件的用法和特性,如果发现组件功能上有bug,那想要自己改正的难度就比应用基于请求的框架的情况下难得多,这时不仅要精通基于请求框架的底层的(underlying)概念和技术,还要掌握所用的基于组件框架的内部知识。
选择基于请求的和基于组件的两种框架各有优劣。虽然一眼看上去后者有很大的吸引力,普通的web开发人员只要使用专门的公司或开源组织提供的组件就可以轻松开发出好用漂亮的界面,但是有几种因素综合起来不利于这种理想中的方案。要编写一个没有潜在问题的、跨浏览器的、显示美观并且有足够灵活性可以调整的服务器端组件是需要高水平的技能、丰富的经验和较多时间的,即使付出这些成本,也不能完全避免使用者失望的情况。
综合来看,基于请求的框架要程序员自己动手的地方比较多,但也因此可以更精细地控制HTML、CSS和Javascript这些最终决定应用程序界面的代码,特别是如果要在界面上有创新,尝试新的视觉效果和用户操作,必然选择基于请求的框架。基于组件的框架可以提高开发界面的效率,前提是选用的组件质量优秀。