2007年9月26日 星期三

Single Page Application - 下一代的Web應用程式

在Web Service, Ajax, Web 2.0, REST等Web應用與技術話題熱潮,帶動許多第二代的Web開發技術成長之後,這些話題也漸漸地消退。不過許多人可能不曾發現,其實這些技術名詞,是在慢慢地顯露一點:Web應用程式逐漸從Server Side轉移到Client Side,也就是瀏覽器身上。

本篇文章要從以往的Server Side Web應用程式,其開發方式與演進來介紹Single Page Application(SPA) 與現今所有主流Web技術。

我在Web 2.0過去,現在與未來介紹Ruby On Rails都有提到一些Web技術的演進,比較明顯的趨勢就是從靜態到動態頁面,而設計的方式也更程式化。而在http://atedev.wordpress.com/2007/01/09/Web2.0:過去,現在及未來也有讀者在前言提到,技術並不是將一個名詞安上去就好。我相當贊同這句話,因此也在這篇文章中希望來做個總整理,以技術及歷史來看看Web是怎樣成長的。

Web應用程式的演進


動態網頁


儘管Web並不是一個三言兩語能拿個版本號碼來解釋,但實際上Web技術確實有些明顯的分隔點。

最早期我們熟知的就是靜態網頁,這應該沒啥問題。儘管在2000前,php,asp就開始流行,坊間的書上也都稱之為動態網頁。而我在此提及的動態網頁程式,實際上卻是從php4釋出的那一年開始(註釋)。這邊要讓大家瞭解的分界點,其實是php4開始被許多商業公司所採用,而軟體的形式也更為套裝化,而不再像之前大家認定的「動態」網頁僅僅只是拿來完成一些簡單的區塊來與一般的靜態網頁整合。


在2004年的時候,Web Framework的產生,創造了Web應用程式另一個新的高峰。而在這個時候也開始有一些Rich Web Client概念的雛形了。我將Ruby On Rails定為一個分界點是因為,他顛覆了傳統動態網頁還在使用設計方式,而改用MVC。但要注意的是Ruby On Rails儘管整合了Ajax與進階Javascript函式庫,但還是沒有改變回傳完整或部分HTML的方式,意思便是HTML的產生始終在Server Side。


Rich Internet Application


一直到現在,有相當多的Web應用程式,都還是維持使用URL來切換各種功能與畫面。而這些以「頁面」為主的程式,並不太需要控制DOM,就不常遇到跨瀏覽器問題。然而自從Firefox逐漸也在市場佔有一席之地,Javascript的應用普及之後,跨瀏覽器問題也接著發生。為了避開各種不同的瀏覽器所帶來的問題,各大企業都獨力發展自己可以嵌入在瀏覽器的應用程式。早期如Java Applet及微軟的的Active X,算是Rich Internet Application(RIA)的開始,但效能方面還是差強人意。

直到2004年的時候,RIA出現了兩種不同的實做方法:一種是承襲以往需要安裝額外Runtime或是在特定瀏覽器才能執行的方式,稱做Sandbox;另一種是只採用CSS,HTML,並以Javascript控制HTML DOM的Dynamic HTML方式,優點就是只需要瀏覽器就可以執行。而後者也延伸出利用Offline Database或是Ajax+Web Service來傳送與儲存程式資料,並可以儲存成一個獨立頁面的Web應用程式,稱做Single Page Application(SPA)。SPA最典型的例子,就是Gmail。Google盡力克服了跨瀏覽器的問題,將Javascript發揮的淋漓盡致,讓大家驚嘆光靠純粹的Web技術竟能做到如此程度。

而我將2005定為Sandbox RIA真正開始的年代,也是因為Adobe併購Macromedia,而有了較完整的開發環境與資源,並不是以往單純地嵌入Flash。這個契機也促使微軟改變策略,比起效能較差的Asp.Net,而拿Sliverlight作為Web下一代主力軍。

RIA或SPA都是學習歷程長,語言多又複雜的Web應用程式技術,也因此發展速度相當緩慢,但不可小看的是這些優點:

  • 相較以往在Server上產生HTML並回傳至瀏覽器,任何畫面皆利用瀏覽器本身或附加的功能來產生。形同於借用了Client Side CPU的運算資源,減少Server成本。使用者感受到的互動性與回應速度皆有大幅的提升。

  • 由於Server並不是每次都回傳複雜龐大的HTML,而是利用XML或JSON傳輸資料的部分,使用的頻寬也相對變小。

  • Server Side除了使用傳統XML Web Service,更可以採用REST,讓Client的應用程式可以更快速掌握資料的新增修改刪除(CRUD)並簡化呼叫的服務URL。

  • 能夠快速Mashup其他的Web應用程式資源,又能擁有高速的執行效能。

下表列出了Web技術的演進,要注意到後三種技術集合,其時間是並行的:

靜態網頁動態網頁程式Web應用程式 Rich Internet Application with Sandbox Single Page Application
時期2000以前2000(php4釋出)~20042004(Ruby On Rails釋出)以後2005(macromedia被adobe併購)以後2004(Gmail釋出beta)以後
表現層CSSCSS,HTML,JavascriptCSS,HTML,JavascriptFlash, SliverlightCSS,HTML(DOM)
邏輯層JavascriptTemplate或自行撰寫Web FrameworkAction Script, C#Javascript或是撰寫Web Service的語言
資料層HTMLDatabase(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以外還需學習一種撰寫Web Service的語言。需要相當熟悉DOM及CSS,也需考慮瀏覽器的差異,開發起來相對地困難許多。

表現層的演進可以得知,不管是RIA利用sandbox或者是SPA利用DHTML作為表現層,相較起來傳統以文字HTML拼湊出畫面的作法已經無法符合使用者的需求。更動態,更彈性的作法才能讓使用者獲得操作感。

而在邏輯層上,演進到了SPA則是變得較為複雜。如果是使用Web Service作為Server Side,除了必須撰寫該語言外,也還是得撰寫Javascript來控制畫面的呈現。而這也影響到開發方式,以往的編輯器多半著重一種主要的語言上,但現在的Web IDE多半都可以完整的處理所有瀏覽器的語言,及多種Server Side語言。例如Aptana IDE就是最好的例子。

資料層的演進就比較簡單,直到RIA的時期,還是相當依賴傳統的Database。但SPA的時期,就可以採用Offline Database,這裡指的就是Google Gears。但如果要使用傳統的Online Database,全部都尚未有Javascript的Client,就必須透過Web Service來轉換資料。而在SQL到ORM的演進上,雖然使用物件導向的作法減緩執行速度,但相對降低開發難度,帶來更大的價值。

Single Page Application


定義


請參考 http://www.answers.com/topic/single-page-application?cat=technology 

Single Page Application是一種Web Application,完全地在瀏覽器上執行。也就是說,標準的SPA程式是不需要網路連線的。

像這些範例,你可以使用firebug來觀察連線的動作。

  • TiddlyWiki http://www.tiddlywiki.com/:這個wiki editor,除了整整一萬行的javascript以外,只有一個很簡單的起始頁。就算你拔了網路線,也只是不能夠儲存而已。但他要如何儲存編輯好的wiki內容呢?必須設定外部伺服器進行同步作業。

  • Protopage http://www.protopage.com/:外型設計得很漂亮,操作感也很順暢。仔細觀察你也會發現在進行網路傳輸的時候都是傳遞JSON。

而畢竟沒有網路連線的程式很難作為應用,所以我在SPA架構一節會提出兩種SPA程式的演進。
在此也必須提出一個概念,SPA並非只是一個「不切換頁面」或是「URL不變」的程式。而SPA所使用的觀念,就如同前言,已經是將瀏覽器當作client端了。
既然是client端,那勢必代表中間是必須傳輸資料,而並非HTML;也因此HTML的產生也會發生在client端,而並非傳統由server產生HTML再由瀏覽器載入。

所依賴的Web技術


AjaxAsynchorous Javascript And XML,早期Ajax被用作來傳輸單純的HTML或是XML,並且利用DOM的innerHTML屬性來更新部分HTML內容。如今Ajax在SPA中被當作重要的傳輸媒介(Transport),無論範本資料到應用程式資料,都是利用Ajax在背景傳輸完後,再由Javascript Template來產生HTML。

JSONJavascript Simple Object Notation,在標準的Javascript語法中,以{ }及[ ]這兩個語法,可以宣告物件與陣列,並可以使用eval函式將他轉換為Javascript物件。例如object=eval("({a:'b'})"),此時object物件便有一個屬性"a"其值為"b"。在SPA中,JSON被運用來作為一種資料格式,藉此取代複雜的XML,以節省頻寬。而傳輸的JSON資料又可以快速還原為javascrip物件,又更節省程式執行的時間。

在SPA中,HTML DOM是一個最重要的元素,尤其是DIV及SPAN等Container的操作更是。由於絕大多數的畫面都不進行任何換頁的動作,程式裡大部分都是在控制DOM及Container。而對於A (Anchor)而言,href裡的URL也沒有太大意義,多半都是在onclick裡寫javascript,或是用Javascript函式庫去bind onclick事件。由於直接呼叫瀏覽器提供的DOM函式庫功能,會遇到像IE一樣不符合W3C規格的問題。要選用一個合適的Javascript擴充函式庫,如Prototype.js或是jQuery,如此才不會有太多跨瀏覽器問題。

CSS在以往的Web應用程式中,多半都拿來當作畫面的修飾,布景主題或是顏色特校。但在SPA中,必須要熟悉CSS的Dimentation(長寬控制),Classification(顯示行為),Positioning(定位)。在無法換頁的狀況下, 只能靠著移動,隱藏,顯示這些方法來控制畫面的元素。如果要瞭解這些進階CSS的主題,都可以在w3school裡的教學找到。

Trimpath是Google為了SPA而開發出來的一個函式庫集合,也可以說是Rails的Javascript版。如果要撰寫上述第一種SPA,就必須利用到Trimpath的全部,而第二種只需要用到Trimpath Javascript Template即可。Javascript Template(JST)如同PHP的Smarty一樣,是標準的範本技術,只是採用的語言是Javascript。為了撰寫SPA,必須要好好地運用JST。

快取的機制在SPA也相當的重要,為了達到讓使用者感受到程式的反應快速,就必須應用多方面的快取。

  • View Cache:SPA中的View就是指已經顯示出來的HTML,很多常用,而不需要經常改變的HTML,就可以將放在Container(DIV或SPAN)裡。不用的時候就隱藏,需要的時候就顯示。而另一種方式是可以用z-index將選單或是清單的Container放在最下層,而要回到這個清單的時候就將蓋在其上的container隱藏。

  • Template Cache:一般來說JST的範本資料只要讀取一次就可以,又因為這些只是字串,可以直接就存在Hash裡。

  • Javascript Cache:在我提出的SPA實做中,有一個特性是將各個JST的「行為」程式碼分開,就如同sap.net將aspx與cs檔分開的作法一樣。而如果採用這個作法,不需要重複讀取的javascript就必須要快取。

  • Data Cache:資料快取是最難的一部份,牽扯到了快取一致性的問題。而現在在javascript中並沒有對於XML或JSON的資料快取解決方案,未來如果能夠有這樣的函式庫,就能夠更提升整體的效率。

以上說明的都是Client Side所必須要使用到的技術,而Server Side的技術多半與Service Design息息相關。

RESTRepresentational State Transfer,他比較像是一種設計樣式(Design Pattern),而不是Web技術。在以往Web應用程式的規劃中,URL並不完全具有意義,傳輸的內容型態多半是HTML,而HTTP的各種動作也並 未完全利用。在SPA中,由於需要在不同時間傳輸各種資料,如HTML範本,JS範本,或是XML及JSON資料。此時REST的設計技巧就可以節省下很 多重複的命名,而讓程式碼整體更有意義。支援REST設計樣式的Web Framework如Ruby On Rails,讓整體設計較為簡單。

SPA的架構


SPA就分類而言,算是RIA的一種,只是不採用任何的sandbox而已。典型的SPA是不需要任何的後端的Web Service或是Offline Database,只需要一個htm檔或是一個網頁就能夠運作,如微軟的HyperText Application(HTA)就是,但還是缺乏完善的資料儲存能力。

spa_01.png

第一種SPA程式,如同著名的Google Reader離線版,具有一個離線資料庫與一個同步管理程式,在有網路連線的時候,會將資料同步回線上的資料庫。這個最大的優點就是完全利用了Client的CPU資源,使用者雖然看見的是網頁,但卻是在使用在本機執行的獨立應用程式,因此速度是相當流暢。比起一般的動態網頁,這樣子的使用體驗更能夠顛覆一般人對於「網頁」的看法,而逐漸瞭解何謂Web應用程式。另一個例子是使用Trimpath函式庫撰寫成的NextAction,是一個多功能的ToDo List。

spa_02.png

另一種就是較簡單的SPA,不具有離線瀏覽的能力,但是承襲了使用javascript的高效能。必須提及的是Server Side並不是採用XML,而是可以快速轉換為Javascript物件的JSON,來當作Web Service。如此Server Side的語言只需要具備能夠快速將物件serialize成JSON的能力即可。

Rails與SPA


這個小節所要說明的是相當技術性的部分,無法說明的太詳細,有興趣的讀者可以寫mail一起討論。為了簡化觀念,我使用Sequence Diagram來說明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。

spa_03.png

  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時的程式碼,接著就可以照一般方式使用Rails Model讀取資料庫。

  7. Model傳回的Ruby物件,當然就要轉換成JSON回傳。而在ruby中相當簡單地便是呼叫to_json就可以轉換了。

  8. 回傳的JSON,併和剛剛快取的JST,使用trimpath javascript template函式庫產生成HTML,並更新至container裡。

  9. 在這裡我採用行為javascript程式碼與範本分離的方式,所以還是以HTTPRequest再次傳送要求,並將Content-Type指定為Javascript。

  10. 同樣地根據要求的content-type,會回傳javascript文件。

  11. 快取,並呼叫eval執行回傳的javascript。

  12. 將container顯示在想要放置的畫面區域。

結論


許多人都在說Web 2.0可能又是另一次的泡沫化,這個熱潮怎樣開始的,又怎樣消退的,也是相當明顯。網路上對於各種新技術名詞的炒作,將不同應用層次的技術,全部攪和在一起說明或稱做是最終解決方案,也模糊了使用者的眼睛。那麼,在這個時代,到底還有什麼可以相信,可以學習的?

唯一能夠做的就是重新審視這些技術,瞭解因果。就可以知道哪些作法是適合用在自己現在的專案,那些是本質相同的,哪些是跨大其詞的。根本的觀念正確,就不需要擔心這些延伸的技術是否會有誤解或誤用。

註釋
在2009年3月的時候,對岸的一位作家周愛民老師有指點了我有關php4在這個時期的定位。周愛民老師最近的大作為「JavaScript语言精髓与编程实践」,其中的第一章的各個第三小節,請各位參考。文章中也提及了那個年代的一些真實的狀況。

但我在這邊還是希望解釋清楚,確實用php,asp來定義「動態網頁」(甚至我還說php4)並不是最正確的說法。但實際上我必須說明這是當時臺灣的書籍普遍描述的名詞與認知,我想台灣的早期的網頁程式設計師們應該對我的描述很有感受吧!

實際上真正的「動態網頁」應該指的是Dynamic HTML (DHTML),表示使用javascript去控制HTML DOM進而直接在網頁上產生動態效果的作法,而並非是server side的任何技術如php,asp,jsp等。但隨著時間演進,DHTML這個字眼也逐漸被人淡忘,變成是Ajax。當時的DHTML也已經有人嘗試著使用javascript與server通訊,那就是與現在的Ajax沒啥不同。至於說php,asp等的為啥會被說成動態網頁,也只是反映出那個時候台灣普遍對網頁設計的認知而已。

我很感謝周老師對我的指點,特此說明當時寫文章的想法。

1 則留言 :

  1. [...] 如果,上面這篇文章讓你覺得不夠,希望能夠更加深入的了解Web的意義,我想, 同一位作者另外一篇文章就相當值得推薦閱讀:「Single Page Application - 下一代的Web應用程式」。這篇文章當中鉅細靡遺的說明了Web應用的架構以及其特色,希望能夠幫助許多自以為了解Web2.0的人去真正的理解到何謂Web幾點幾,而非盲從的跟著別人喊著莫名奇妙的名詞,還包括那種自以為可以隨便升級高喊自己是Web3.0先驅的那種人…. [...]

    回覆刪除