Single Page Application - 下一代的Web应用程序
在Web Service, Ajax, Web 2.0,REST等Web应用与技术话题热潮,带动许多第二代的Web开发技术成长之后,这些话题也渐渐地消退。不过许多人可能不曾发现,其实这些技术名词,是在慢慢地显露一点:Web应用程序逐渐从ServerSide转移到Client Side,也就是浏览器身上。
本篇文章要从以往的Server Side Web应用程序,其开发方式与演进来介绍Single PageApplication(SPA) 与现今所有主流Web技术。
我在Web2.0过去,现在与未来及介绍Ruby OnRails都有提到一些Web技术的演进,比较明显的趋势就是从静态到动态页面,而设计的方式也更程序化。而在http://atedev.wordpress.com/2007/01/09/Web2.0:过去,现在及未来/也有读者在前言提到,技术并不是将一个名词安上去就好。我相当赞同这句话,因此也在这篇文章中希望来做个总整理,以技术及历史来看看Web是怎样成长的。
Web应用程序的演进
动态网页
尽管Web并不是一个三言两语能拿个版本号码来解释,但实际上Web技术确实有些明显的分隔点。
最早期我们熟知的就是静态网页,这应该没啥问题。尽管在2000前,php,asp就开始流行,坊间的书上也都称之为动态网页。而我在此提及的动态网页程序,实际上却是从php4释出的那一年开始。这边要让大家了解的分界点,其实是php4开始被许多商业公司所采用,而软件的形式也更为套装化,而不再像之前大家认定的「动态」网页仅仅只是拿来完成一些简单的区块来与一般的静态网页整合。
在2004年的时候,WebFramework的产生,创造了Web应用程序另一个新的高峰。而在这个时候也开始有一些Rich WebClient概念的雏形了。我将Ruby OnRails定为一个分界点是因为,他颠覆了传统动态网页还在使用设计方式,而改用MVC。但要注意的是Ruby OnRails尽管整合了Ajax与进阶Javascript函式库,但还是没有改变回传完整或部分HTML的方式,意思便是HTML的产生始终在ServerSide。
Rich Internet Application
一直到现在,有相当多的Web应用程序,都还是维持使用URL来切换各种功能与画面。而这些以「页面」为主的程序,并不太需要控制DOM,就不常遇到跨浏览器问题。然而自从Firefox逐渐也在市场占有一席之地,Javascript的应用普及之后,跨浏览器问题也接着发生。为了避开各种不同的浏览器所带来的问题,各大企业都独力发展自己可以嵌入在浏览器的应用程序。早期如JavaApplet及微软的的Active X,算是Rich InternetApplication(RIA)的开始,但效能方面还是差强人意。
直到2004年的时候,RIA出现了两种不同的实做方法:一种是承袭以往需要安装额外Runtime或是在特定浏览器才能执行的方式,称做Sandbox;另一种是只采用CSS,HTML,并以Javascript控制HTMLDOM的Dynamic HTML方式,优点就是只需要浏览器就可以执行。而后者也延伸出利用OfflineDatabase或是Ajax+WebService来传送与储存程序数据,并可以储存成一个独立页面的Web应用程序,称做Single PageApplication(SPA)。SPA最典型的例子,就是Gmail。Google尽力克服了跨浏览器的问题,将Javascript发挥的淋漓尽致,让大家惊叹光靠纯粹的Web技术竟能做到如此程度。
而我将2005定为SandboxRIA真正开始的年代,也是因为Adobe并购Macromedia,而有了较完整的开发环境与资源,并不是以往单纯地嵌入Flash。这个契机也促使微软改变策略,比起效能较差的Asp.Net,而拿Sliverlight作为Web下一代主力军。
RIA或SPA都是学习历程长,语言多又复杂的Web应用程序技术,也因此发展速度相当缓慢,但不可小看的是这些优点:
· 相较以往在Server上产生HTML并回传至浏览器,任何画面皆利用浏览器本身或附加的功能来产生。形同于借用了Client SideCPU的运算资源,减少Server成本。用户感受到的互动性与响应速度皆有大幅的提升。
· 由于Server并不是每次都回传复杂庞大的HTML,而是利用XML或JSON传输数据的部分,使用的带宽也相对变小。
· Server Side除了使用传统XML WebService,更可以采用REST,让Client的应用程序可以更快速掌握数据的新增修改删除(CRUD)并简化呼叫的服务URL。
· 能够快速Mashup其他的Web应用程序资源,又能拥有高速的执行效能。
下表列出了Web技术的演进,要注意到后三种技术集合,其时间是并行的:
| 静态网页 | 动态网页程序 | Web应用程序 | Rich Internet Application with Sandbox | Single Page Application |
时期 | 2000以前 | 2000(php4释出)~2004 | 2004(Ruby On Rails释出)以后 | 2005(macromedia被adobe并购)以后 | 2004(Gmail释出beta)以后 |
表现层 | CSS | CSS,HTML,Javascript | CSS,HTML,Javascript | Flash, Sliverlight | CSS,HTML(DOM) |
逻辑层 | Javascript | Template或自行撰写 | Web Framework | Action Script, C# | Javascript或是撰写Web Service的语言 |
资料层 | HTML | Database(SQL) | Database(ORM) | Database(ORM) | Offline Database, Web Service |
开发方式 | 网页编辑程序 | 整合HTML及Server Side语言的编辑器 | 整合Web Framework的IDE | 整合Sandbox的IDE | 整合Server Side与Client Side语言的IDE |
运算资源 | 所有数据直接透过Web Server送出,除了硬盘读取,几乎不需要额外的运算 | 因为使用了Server Side语言来Render表现层,运算多半会消耗在Server | 因为使用了Server Side语言来Render表现层,运算多半会消耗在Server | 运算资源平均被分散在Server及Client,但Client需要Sandbox去执行,所以会消耗更多CPU资源 | 运算资源平均被分散在Server及Client |
数据格式 | 传送完整的HTML | 传送完整的HTML | 传送部分或完整的HTML | 只需第一次传送HTML及内嵌程序(Flash或Sliverlight),其余传送XML | 只需第一次传送HTML及Javascript,其余可传送XML或JSON |
优点 | 简单易学 | 学习同一种Server Side语言,搭配简单的HTML,CSS,JS观念便能够有成果 | 整合Ajax或进阶Javascript函式库,REST及MVC。使得设计概念更为面向对象化 | 用户接口反应快速,变化多且美观。兼顾窗口程序的反应速度,且能部分兼容传统HTML应用。 | 完全兼容传统HTML应用,及任何可能的Web应用程序Mashup。可以采用不同的传输方式,并和REST及浏览器快取来节省带宽,使得整体反应相当快速。 |
缺点 | 无法让用户储存任何应用程序数据,任何数据必须藉由人工设计 | 对于庞大的应用程序,便得花上大量的Server成本。程序反应速度受限于服务器负载,需要丛集架构来弥补。 | 设计方式更为简单快速,但相对于传统的动态网页程序付出更大的Server成本。 | 除了CSS,HTML,JS以外还需学习一两种不同的语言才能进行开发。RIA通常程序数据较大,在开始使用前必须等候一段下载时间。 | 除了CSS,HTML,JS以外还需学习一种撰写WebService的语言。需要相当熟悉DOM及CSS,也需考虑浏览器的差异,开发起来相对地困难许多。 |
表现层的演进可以得知,不管是RIA利用sandbox或者是SPA利用DHTML作为表现层,相较起来传统以文字HTML拼凑出画面的作法已经无法符合使用者的需求。更动态,更弹性的作法才能让使用者获得操作感。
而在逻辑层上,演进到了SPA则是变得较为复杂。如果是使用Web Service作为ServerSide,除了必须撰写该语言外,也还是得撰写Javascript来控制画面的呈现。而这也影响到开发方式,以往的编辑器多半着重一种主要的语言上,但现在的WebIDE多半都可以完整的处理所有浏览器的语言,及多种Server Side语言。例如Aptana IDE就是最好的例子。
数据层的演进就比较简单,直到RIA的时期,还是相当依赖传统的Database。但SPA的时期,就可以采用OfflineDatabase,这里指的就是Google Gears。但如果要使用传统的OnlineDatabase,全部都尚未有Javascript的Client,就必须透过WebService来转换数据。而在SQL到ORM的演进上,虽然使用面向对象的作法减缓执行速度,但相对降低开发难度,带来更大的价值。
Single Page Application
定义
请参考 http://www.answers.com/topic/single-page-application?cat=technology
Single Page Application是一种WebApplication,完全地在浏览器上执行。也就是说,标准的SPA程序是不需要网络联机的。
像这些范例,你可以使用firebug来观察联机的动作。
· TiddlyWiki http://www.tiddlywiki.com/:这个wikieditor,除了整整一万行的javascript以外,只有一个很简单的起始页。就算你拔了网络线,也只是不能够储存而已。但他要如何储存编辑好的wiki内容呢?必须设定外部服务器进行同步作业。
· Protopage http://www.protopage.com/:外型设计得很漂亮,操作感也很顺畅。仔细观察你也会发现在进行网络传输的时候都是传递JSON。
而毕竟没有网络联机的程序很难作为应用,所以我在SPA架构一节会提出两种SPA程序的演进。
在此也必须提出一个概念,SPA并非只是一个「不切换页面」或是「URL不变」的程序。而SPA所使用的观念,就如同前言,已经是将浏览器当作client端了。
既然是client端,那势必代表中间是必须传输数据,而并非HTML;也因此HTML的产生也会发生在client端,而并非传统由server产生HTML再由浏览器加载。
所依赖的Web技术
Ajax为Asynchorous Javascript AndXML,早期Ajax被用作来传输单纯的HTML或是XML,并且利用DOM的innerHTML属性来更新部分HTML内容。如今Ajax在SPA中被当作重要的传输媒介(Transport),无论模板数据到应用程序数据,都是利用Ajax在背景传输完后,再由JavascriptTemplate来产生HTML。
JSON为Javascript Simple ObjectNotation,在标准的Javascript语法中,以{ }及[]这两个语法,可以宣告对象与数组,并可以使用eval函式将他转换为Javascript对象。例如object=eval_r(”({a:’b'})”),此时object对象便有一个属性”a”其值为”b”。在SPA中,JSON被运用来作为一种数据格式,藉此取代复杂的XML,以节省带宽。而传输的JSON数据又可以快速还原为javascrip对象,又更节省程序执行的时间。
在SPA中,HTMLDOM是一个最重要的元素,尤其是DIV及SPAN等Container的操作更是。由于绝大多数的画面都不进行任何换页的动作,程序里大部分都是在控制DOM及Container。而对于A(Anchor)而言,href里的URL也没有太大意义,多半都是在onclick里写javascript,或是用Javascript函式库去bindonclick事件。由于直接呼叫浏览器提供的DOM函式库功能,会遇到像IE一样不符合W3C规格的问题。要选用一个合适的Javascript扩充函式库,如Prototype.js或是jQuery,如此才不会有太多跨浏览器问题。
CSS在以往的Web应用程序中,多半都拿来当作画面的修饰,布景主题或是颜色特校。但在SPA中,必须要熟悉CSS的Dimentation(长宽控制),Classification(显示行为),Positioning(定位)。在无法换页的状况下,只能靠着移动,隐藏,显示这些方法来控制画面的元素。如果要了解这些进阶CSS的主题,都可以在w3school里的教学找到。
Trimpath是Google为了SPA而开发出来的一个函式库集合,也可以说是Rails的Javascript版。如果要撰写上述第一种SPA,就必须利用到Trimpath的全部,而第二种只需要用到TrimpathJavascript Template即可。JavascriptTemplate(JST)如同PHP的Smarty一样,是标准的模板技术,只是采用的语言是Javascript。为了撰写SPA,必须要好好地运用JST。
快取的机制在SPA也相当的重要,为了达到让用户感受到程序的反应快速,就必须应用多方面的快取。
· ViewCache:SPA中的View就是指已经显示出来的HTML,很多常用,而不需要经常改变的HTML,就可以将放在Container(DIV或SPAN)里。不用的时候就隐藏,需要的时候就显示。而另一种方式是可以用z-index将选单或是清单的Container放在最下层,而要回到这个清单的时候就将盖在其上的container隐藏。
· Template Cache:一般来说JST的模板数据只要读取一次就可以,又因为这些只是字符串,可以直接就存在Hash里。
· JavascriptCache:在我提出的SPA实做中,有一个特性是将各个JST的「行为」程序代码分开,就如同sap.net将aspx与cs档分开的作法一样。而如果采用这个作法,不需要重复读取的javascript就必须要快取。
· DataCache:资料快取是最难的一部份,牵扯到了缓存一致性的问题。而现在在javascript中并没有对于XML或JSON的数据快取解决方案,未来如果能够有这样的函式库,就能够更提升整体的效率。
以上说明的都是Client Side所必须要使用到的技术,而ServerSide的技术多半与Service Design息息相关。
REST为Representational StateTransfer,他比较像是一种设计样式(DesignPattern),而不是Web技术。在以往Web应用程序的规划中,URL并不完全具有意义,传输的内容型态多半是HTML,而HTTP的各种动作也并未完全利用。在SPA中,由于需要在不同时间传输各种数据,如HTML模板,JS模板,或是XML及JSON数据。此时REST的设计技巧就可以节省下很多重复的命名,而让程序代码整体更有意义。支持REST设计样式的Web Framework如Ruby OnRails,让整体设计较为简单。
SPA的架构
SPA就分类而言,算是RIA的一种,只是不采用任何的sandbox而已。典型的SPA是不需要任何的后端的WebService或是Offline Database,只需要一个htm档或是一个网页就能够运作,如微软的HyperTextApplication(HTA)就是,但还是缺乏完善的数据储存能力。
第一种SPA程序,如同著名的GoogleReader脱机版,具有一个脱机数据库与一个同步管理程序,在有网络联机的时候,会将数据同步回在线的数据库。这个最大的优点就是完全利用了Client的CPU资源,使用者虽然看见的是网页,但却是在使用在本机执行的独立应用程序,因此速度是相当流畅。比起一般的动态网页,这样子的使用体验更能够颠覆一般人对于「网页」的看法,而逐渐了解何谓Web应用程序。另一个例子是使用Trimpath函式库撰写成的NextAction,是一个多功能的ToDoList。
另一种就是较简单的SPA,不具有脱机浏览的能力,但是承袭了使用javascript的高效能。必须提及的是ServerSide并不是采用XML,而是可以快速转换为Javascript对象的JSON,来当作Web Service。如此ServerSide的语言只需要具备能够快速将对象serialize成JSON的能力即可。
Rails与SPA
这个小节所要说明的是相当技术性的部分,无法说明的太详细,有兴趣的读者可以写mail一起讨论。为了简化观念,我使用SequenceDiagram来说明Rails要如何应用在SPA上:
· URLRequester是一个javascript函式,主要的工作就是以REST方式对一个URL进行不同Content-Type的Request,并且将回传的资料产生HTML,并填到container里显示出来。
· HTTPRequest在这里作为进行Ajax呼叫的传输媒介。
· RailsController表示服务器端对应至特定URL的程序,在这里也必须使用REST方式来响应。也因此如果要求content-type为HTML的时候就传JST模板,要求JSON的时候就传数据,要求javascript的时候就传javascript文件。
· RailsModel代表服务器端的数据库,在要求数据的时候,势必定要连数据库来取得数据的。而回传的时候,就将ruby对象serialize成JSON。
1. Client呼叫URLRequester函式,例如http://servername/controller/action/id。
2. HTTPRequest送出一个要求,并指定Content-Type为HTML。
3. RailsController接到指定的URL,并执行controller#action。我将JST写在rhtml里面,JST基本上也是html,不过是模板的卷标换成{}而已。因为REST设计方式会因为指定的content-type回传对应的型态,此时直接将内容的JST文件传回。
4. 在前面我有提及快取的重要性,所以这里就快取住这个JST,下次要求同样的内容就可以直接使用而不用重复传输。
5. 对于同一个URL,使用HTTPRequest送出要求,并指定Content-Type为JSON。
6. 因为REST的特性,这次会执行到content-type为JSON时的程序代码,接着就可以照一般方式使用RailsModel读取数据库。
7. Model传回的Ruby对象,当然就要转换成JSON回传。而在ruby中相当简单地便是呼叫to_json就可以转换了。
8. 回传的JSON,并和刚刚快取的JST,使用trimpath javascripttemplate函式库产生成HTML,并更新至container里。
9. 在这里我采用行为javascript程序代码与范本分离的方式,所以还是以HTTPRequest再次传送要求,并将Content-Type指定为Javascript。
10. 同样地根据要求的content-type,会回传javascript文件。
11. 快取,并呼叫eval执行回传的javascript。
12. 将container显示在想要放置的画面区域。
结论
许多人都在说Web2.0可能又是另一次的泡沫化,这个热潮怎样开始的,又怎样消退的,也是相当明显。网络上对于各种新技术名词的炒作,将不同应用层次的技术,全部搅和在一起说明或称做是最终解决方案,也模糊了使用者的眼睛。那么,在这个时代,到底还有什么可以相信,可以学习的?
唯一能够做的就是重新审视这些技术,了解因果。就可以知道哪些作法是适合用在自己现在的项目,那些是本质相同的,哪些是跨大其词的。根本的观念正确,就不需要担心这些延伸的技术是否会有误解或误用。