QuirksBlog:
The AJAX response: XML, HTML, or JSON?
早先這篇文章在TSS上貼出的時候,我很快的瀏覽,便一眼看出這篇文章作者所處的角度。事實上,AJAX概念的不完整和不嚴密性——異步的JavaScript + XML——導致作者將AJAX的輸出分為三種類型:XML, HTML片斷和JSON對象字符串。
首先看XML。對于RPC的數據傳輸,XML從來都是當仁不二的選擇。對于將對象序列化為XML字符串的方式,往往有兩種選擇,一種是將對象本身的屬性作為節點進行輸出,一種是利用語言的元數據特性進行序列化輸出。兩者存在較大不同。對于第一種,輸出案例如下:
<books>
<book>
<title>JavaScript, the Definitive Guide</title>
<publisher>O'Reilly</publisher>
<author>David Flanagan</author>
<cover src="/images/cover_defguide.jpg" />
<blurb>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</blurb>
</book>
<book>
<title>DOM Scripting</title>
<publisher>Friends of Ed</publisher>
<author>Jeremy Keith</author>
<cover src="/images/cover_domscripting.jpg" />
<blurb>Praesent et diam a ligula facilisis venenatis.</blurb>
</book>
</books>
而對于第二種,輸出案例如下:
<list>
<type>java.util.List</type>
<map>
<type>yourapp.domain.Book</type>
<string>title</string>
<string>JavaScript, the Definitive Guide</string>
<string>publisher</string>
<string>O'Reilly</string>
<string>author</string>
<string>David Flanagan</string>
<string>cover</string>
<string>/images/cover_defguide.jpg</string>
<string>blurb</string>
<string>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</string>
</map>
<map>
<type>yourapp.domain.Book</type>
<string>title</string>
<string>DOM Scripting</string>
<string>publisher</string>
<string>Friends of Ed</string>
<string>author</string>
<string>Jeremy Keith</string>
<string>cover</string>
<string>/images/cover_domscripting.jpg</string>
<string>blurb</string>
<string>Praesent et diam a ligula facilisis venenatis.</string>
</map>
</list>
前一種一般來說是同一進程內(同一JVM內)對對象進行序列化和反序列化的方法,在XML-Java綁定一類的框架中比較多見,例如XStream,
JiBX, Castor等等。當同一進程內能夠找到對象具備的正確類型時,這種序列化/反序列化設計和實現都不太困難,而且針對空值處理也比較容易。
可以看出,由于這種方式成本較低,有大量成熟的開源庫可用,加上在AJAX中的X(ML)的“理論”指導下,許多開發者采用這種方式將對象輸出給前臺瀏覽
器,然后利用瀏覽器的XML能力進行輸出顯示。從那篇文章中可以看出,這種工作是純粹的dirty work,
并且由于領域模型或者DTO的不同,輸出的xml結構含義也不同,在對空值的處理上更是麻煩,在處理顯示邏輯的時候,基本上很難在客戶端對以這種方式傳遞
的XML以一種統一的方式進行還原。以這種方式來進行AJAX開發,小規模應用還不能顯出弊端,但是大規模應用出現,領域對象較多時,必然出現怨言。而我
們使用AJAX的真實意圖并非來無趣地去解析各種各樣的XML,而是需要XML中代表的數據。至于XML是什么樣子,除了調試階段,沒有人會關心。
第二種XML的序列化方式是絕大多數跨進程遠程調用協議/框架所采取的方式。SOAP/WebService如此,XMLRPC如此,Buffalo采用
的Burlap也如此。這種遠程調用的特征是,在協議約定的條件下,調用方和被調用方需要保證數據語義的完整性。拿什么保證?就是數據定義信息了。這些協
議的共同特征是采用謀一些標記來描述數據類型:int, long, float, string,
list...這樣定義完成后,只要根據協議,任何語言都能以一種通用的方式對數據進行還原。AJAX引擎的概念也就漸漸呈現——通過某一種協議,他就處
于中間的位置,負責將調用方的請求包裝為協議,轉化為執行方能夠理解的動作加以執行;然后將執行結果轉化為協議的值,傳遞給客戶端,客戶端引擎將協議內容
解析為能夠理解的對象結構傳遞給客戶端,然后就可以利用這個數據來顯示了。XML-RPC如此,WebService如此,DWR如此,JSON如此,
Buffalo也如此。
綜上所述,純粹使用領域模型特定的輸出XML傳遞給客戶端是一種相當原始的做法。他只在很低的層次上印證了所謂AJAX的概念。然而,這種概念的深入思維結果便是一個AJAX引擎。
文中提到的第二種輸出方式:HTML——應該被看作WEB的一個特例,應該說這是歷史因素、瀏覽器能力、設計者等多方因素達到的一個平衡。許多歷史應用
中,大多采用將頁面進行一定規則的分割,然后include或者jsp 2.0
tagfile的方式對公共區域進行處理,留下一小部分進行動態顯示。這里舉一個例子:查詢,顯示書籍列表。傳統做法就是上面一個搜索條件輸入文本框,下
面是搜索結果列表,處于同一個頁面。原來的搜索方式每次提交都要刷新整個頁面,用戶體驗不太好,現在需要改進。按照激進的Ajax做法應該是當搜索按鈕點
擊時,調用bookSearchService.search(String
terms)的方法,取得結果列表,然后Ajax引擎處理結果數據,將數據反序列化為javascript對象,開發者利用這些對象,要么利用DOM,
要么利用JavaScript Template, 在頁面對搜索結果進行展示。然而,問題出現了:
搜索結果太多了,用DOM操作速度太慢;
開發者人手不夠,沒有時間,而這個頁面以前寫過;
那么出現這種情況時,很可能的做法便是,見原來那個搜索結果頁面刨去其他不相干的部分,留下搜索結果部分,然后利用一個resultDiv.innerHTML=xmlhttp.responseText完事。這樣做,既達到了改善體驗的效果,也提高了開發速度。
輸出HTML另外一種方式文中也沒有提及。事實上,HTML不僅僅作為片斷,更重要的是作為頁面視圖的一部分。在buffalo的demo中,可以看到所
有的頁面都被管理起來了,buffalo接管了視圖的切換;這種設計也存在于gmail/163新版郵箱中。這個應用比上面的HTML片斷的使用方式還要
重要,因為這里是緩存可以介入的地方。通過不同的緩存策略,可以將用戶的實際和心理等待時間大大減少,從而進一步改善用戶操作體驗,提升產品競爭力。
(PS. 在Buffalo 1.3中將加入客戶端緩存模塊,無需你動手,buffalo為你緩存視圖)
文中提及的第三種方式,JSON,根據第一部分的描述,應該比較清楚了。實際上他扮演了一個Ajax引擎的角色。這里不得不提的是,使用JSON的相當危
險的。因為他的協議表現與語言本身綁定太死。如果某一天,JSON協議變化了,那么使用JSON的應用基本上不可能應對這個變化——因為返回結果是
eval()得到的。而Buffalo將協議隱藏起來了,1.2版本甚至連服務器端的ServiceInvoker都將burlap實現依賴隔離開。現在
使用burlap,也許某一天不用了,buffalo的應用照樣可以運行。因為里面的細節都是透明的,作為開發者,你只需要關注數據對象本身,而非用來描
述對象的那一堆字符。