Web
顯示層技術(shù)評估
名詞界定
顯示層的意思就是
Presentation
Layer
,也翻譯成表現(xiàn)層、展現(xiàn)層、展示層。
本文討論的范圍只包括采用
HTML
Template
的顯示層技術(shù),不包括
Echo
,
GWT(google web toolkit)
等根據(jù)代碼產(chǎn)生
HTML
的工具。
本文主要討論
Server
Side (
針對
Java
Language)
的顯示層技術(shù),然后進(jìn)一步討論
Browser
Side
(
Ajax
)的顯示層技術(shù)(一個典型的
Ajax
應(yīng)用也分為
Model, View, Controller – Data,
HTML/CSS, JavaScript
)。注意,本文關(guān)于
Ajax
的討論只有很少一部分,因?yàn)槲也簧瞄L這個領(lǐng)域。只是一個順便的擴(kuò)展比較。
一個很有趣的現(xiàn)象。
Server
Side
和
Browser Side
的顯示層技術(shù)格局恰好相反。
Server Side
的
Scripted Template
技術(shù)比較多,比較流行;而
Browser Side
的
HTML DOM Manipulation
技術(shù)、
HTML View Model
技術(shù)比較多,比較流行。
本文會提到一些技術(shù)、或者框架的名稱,但只局限于討論該技術(shù)、該框架的顯示相關(guān)部分的內(nèi)容,而不涉及評估其他方面的特性。比如,本文不討論
Link URL Generation, Action URL Generation
,
Button Script Generation
這些頁面組件事件機(jī)制的方面。
本文是一個深度討論。不討論簡單的替換個字符串的
Hello World
案例,而是窮盡各種顯示層技術(shù)的能力極限,探索它們在復(fù)雜布局(動態(tài)
include
等)、復(fù)雜顯示邏輯(條件、循環(huán)、嵌套、遞歸)等方面的功能。
?
對了,(考慮到
Site
Mesh
,
Struts Tiles
Taglib
等技術(shù)廣泛的群眾基礎(chǔ)),可能需要專門提一下,本文將不討論
Site Mesh
,
Tiles
等布局技術(shù)(
named
include
)。
Site Mesh
相當(dāng)于
XSL
的一個簡化版本,只保留了根據(jù)
(name->file)
配置替換某個
HTML Node
的能力,其余的如
Tiles
,也大致如此,由于多了一個
(name->file)
配置文件,比直接
include file
高級了不少。
由于使用簡單(功能自然也簡單),這類技術(shù)獲得了廣大群眾的支持,呼聲很高。本文為忽略了這一類技術(shù)感到很遺憾。
?
另外需要指出的是,并不存在一個十全十美的方案。
工作總是要做的,不是在
Template
里面做,就是在
Java Code
里面做,總之,總要找個地方做這個工作,天下沒有免費(fèi)的午餐。一方面特性增強(qiáng)了,自然影響到另一方面。
正如,代碼的耦合實(shí)際上并不能完全消除,我們只能把這些耦合點(diǎn)移動來移動去,今天我看這里不舒服了,把耦合點(diǎn)移動到另一個地方;明天另一個人看到那里不舒服了,又移動回來。而且各自都能說出一大堆道理。
所以,需要注意的是,并不存在一個絕對的優(yōu)勝方案。本文只是列出各種技術(shù)指標(biāo)的參考評估數(shù)據(jù),以便幫助讀者根據(jù)自己的需要,做出比較準(zhǔn)確的評估。(是的,準(zhǔn)確的量化評估,而不是廣告語或者口號)
理論模型
一個顯示的整個過程,如果用一個函數(shù)來描述,那么看起來大概是這樣。
F(Data, Template, Display Logic) =>
Result HTML
其中的
Display
Logic
,就是顯示邏輯。
Display
Logic
操作
Data
和
Template
,產(chǎn)生最終結(jié)果。
這個
Display
Logic
可能以各種形式出現(xiàn)在任何地點(diǎn)。
比如,可能作為
Server
Side Script
存在于
Template
里面,把
Data
取出來輸出;也可能存在于后臺
Java
里面,根據(jù)
Data
操作
Template Node
。
針對前一種情況,函數(shù)公式表達(dá)是:
Template Script (Data) => Result
針對后一種情況,函數(shù)公式表達(dá)是:
Logic (Data, Template) => Result
?
這個模型可以作為顯示層技術(shù)的劃分標(biāo)準(zhǔn)。
(1) Scripted Template
HTML
和
Server Side Script
混雜在一起的顯示層技術(shù)。
包括
JSP,
Velocity, Freemarker, Taglib, Tapestry, XSL
等。
肯定有人對這個劃分有異議。
XSL
里面有
choose, if, for
。這還好說。尤其是對
Taglib, Tapestry
,反映可能更加強(qiáng)烈。我似乎已經(jīng)看到,
Taglib or Tapestry
Fans
已經(jīng)跳起來了,高叫著,
Taglib
or Tapestry
明明是組件技術(shù),組件技術(shù),組件技術(shù)
….
這里我還是表示很遺憾。在目前定義的這個狹義模型下,任何
Template
中包含
Logic
的顯示技術(shù)都劃為
Script
這一類。而且在表示邏輯的時候,這類組件技術(shù)表現(xiàn)的更加突出一些。
比如
Tapestry
的
<foreach> <if><if-not>
<let><set>
等邏輯標(biāo)簽。尤其是這個
if not
,是專門多出來的一個條件語句,一般的編程語言里面都不具備這樣的對應(yīng)語法。當(dāng)然,
Tapestry
并不專美,
Taglib
的
Logic Tag
也是如此。
?
(2)Template Manipulation
Java
代碼直接操作
Template
(比如,
HTML DOM
)產(chǎn)生結(jié)果的顯示層技術(shù)。
包括
XMLC,
JDynamiTe, Rife
等。
大家對這一類技術(shù)可能不是很熟悉。后面進(jìn)行特性分析的時候,會舉出一些典型的例子,來說明各自的用法。
一個很有意思的現(xiàn)象是,在
Browser
Side
(
Ajax
),由于
Java Script
操作
HTML DOM
非常方便,這類顯示技術(shù)非常普遍。相反的,
Scripted
Template
的技術(shù),在
Browser
Side
卻不多見。后面討論
Browser
side
的時候,會列舉一些典型的例子。
?
(3) Model Match
Java
代碼負(fù)責(zé)提供符合顯示層要求的
Data Model
,顯示層框架本身把
Data Model
和
Template
進(jìn)行匹配,產(chǎn)生結(jié)果。
包括
Wicket,
Fastm, DOMPlus,
等。
Wicket
如同
Swing
的用法,需要為不同的
UI
Component
提供不同的
View
Model
,比如
Table,
List, Label
等。
Fastm,
DOMPlus
支持
POJO
,但同樣需要滿足一些框架特有的約定。
也許有人會說,某些
Display
Tag Lib, Tapestry Components
可能也需要
Java Code
提供特殊的
View Data Model
。
不過,需要特殊的
View
Data Model
,并不是一個好的特性,意味著不支持
POJO
。
數(shù)據(jù)尋址
在正式開始之前,先說明一下數(shù)據(jù)尋址的概念。
數(shù)據(jù)尋址,意思是數(shù)據(jù)訪問、屬性獲取等。主要包括兩類風(fēng)格。
(1) OGNL Style
http://www.ognl.org/
OGNL (Object Graph Navigation Language)
如此著名和深入人心,以至于我在這里用
OGNL Style
代表
Java Bean
屬性尋址的方式。
比如,
a.b[1].c.d[2].name
另一類當(dāng)然是
(2)XPath Style
比如,
a/b[1]/c/[d]/@name
XPath Style
主要應(yīng)用在
XSL
中。
一個
JXPath
項(xiàng)目能夠按照
XPath
的方式訪問
Java Bean
屬性。
http://jakarta.apache.org/commons/jxpath/
?
簡單的尋址,
OGNL
和
XPath
能夠?qū)?yīng)起來。但是,
OGNL
和
XPath
都各自是功能很強(qiáng)大的語言,復(fù)雜的用法并不能對應(yīng)。
評估指標(biāo)
下面列出一系列比較詳細(xì)的、能夠落到實(shí)處的、能夠客觀量化的、可操作的評估硬指標(biāo)。
排名不分先后。大家可以參考各自關(guān)心的選項(xiàng)。
雖然下面主要針對的都是
Java
Web
顯示技術(shù),但這些指標(biāo)同樣適用于其他語言的
Web
顯示技術(shù)。
評分采取
10
分作為滿分。
(1) Host Language Consistency
宿主語言一致性
Server Side Template Script
和
Server Host Language
是同一種語言。這應(yīng)該是專門針對
JSP
的優(yōu)勢來說了。
JSP
能夠獲得
10
分。
另外,
XSL
也是。
XSL
本是也是
XML
格式。也能夠獲得
10
分。
其他的
Template
Script
,如
taglib,tapestry
只能獲得
0
分。
freemarker, velocity
由于具有一定的動態(tài)解釋的方便特性,可以獲得
2
分。
至于在
Java Code
里面操作
Template
或者提供匹配數(shù)據(jù)的那些技術(shù),由于
Template
中不存在
Script Logic
,能夠獲得
5
分。
大家可能不太注意這個特性。但是這個特性還是有一些意義的。其他的如
ASP.net
,還有動態(tài)語言,
Ruby, Python, PHP, Perl
等,都是
Template Script
和宿主語言一致。
這能夠一定程度上降低學(xué)習(xí)成本。尤其是宿主語言比較適合作為
Script
的情況下。
?
(2)Template Purity
模板純凈度
這主要是指
Template
里面沒有
Script Logic
代碼污染。
這方面,所有的
Scripted
Template
技術(shù)都只能獲得
0
分。
XMLC
能夠獲得
10
分,只利用
HTML
本身的
Attribute
,沒有增加任何自定義
DOM Attribute
。
Wicket, DOMPlus
能夠獲得
9
分,它們增加了一部分自定義
DOM Attribute
。
JDynamiTe, Fastm
能夠獲得
7
分,它們采用了
XML Comment
作為自定義結(jié)構(gòu)標(biāo)簽。
Rife
也能夠獲得
3 -- 7
分,具體看它采用什么標(biāo)簽格式。
(3)Template Tidiness
模板整潔度
主要是指
Template
的格式是否整齊規(guī)范。
Taglib,?XSL
無疑是勝利者,本身就是
XML
格式,通用的
XML Parser
就可以解析它們,比較容易在
IDE Plugin
中處理。
XMLC, Taglib, XSL
能夠獲得
10
分。
Tapestry, Wicket, DOMPlus
也能夠獲得
10
分,同樣是
XML
格式。
JDynamiTe, Fastm, Rife
能夠獲得
5
分。
JSP, Velocity, Freemarker
只能獲得
0
分。
(4) Replacement Flexibility
替換靈活度
主要是指能否自由替換
Template
里面的任何一塊文本。不用考慮
DOM Node
。
JSP, Freemarker, Velocity, Rife,
JDynamicTe, Fastm
無疑是勝利者,毫無限制,能夠獲得
10
分。
Taglib, XSL, Tapestry, Wicket, XMLC,
DOMPlus
都或多或少受到
DOM
Node
的限制(解析的最小單位是
XML
Node
),能夠獲得
6
分。
(5)WYIWYG
所見即所得
Template
能夠在
Browser
里面直接大致正確顯示,設(shè)計(jì)人員友好。
XMLC, DOMPlus
得分
10
。
Wicket
得分
9
。
JDynamiTe, Fastm, (Rife
根據(jù)情況
)
得分
8
。
Tapestry
得分
7
。
HTML
畢竟夾雜了
Logic Tag
。
JSP, Freemarker, Velocity, Taglib, XSL
得分
0
。
?
Freemarker, Velocity
屬于按行解析,有可能采取如下手段,把語句包含在
XML Comment
里面,進(jìn)行顯示友好的處理。這種情況下得分
5
。
<!--
#if ….
-->
?
由于
Taglib
的
XML
規(guī)范格式,使得某些
IDE Plugin
,
DreamWeaver Plugin
能夠顯示
HTML Display Taglib
。如果是對于此類
Plugin
來說,
Taglib
的所見即所得分?jǐn)?shù)可以是
0-- 5
分。類似于
Tapestry
,仍然是
Logic Tag
影響了最終得分。
(6)Action Code Purity
用戶代碼純凈度
主要是指用戶提供顯示數(shù)據(jù)的后臺
Java
代碼的純凈度,是否免除了
HTML
,或者
Template
操作的污染。
Servlet
的
HTML
污染現(xiàn)象就非常嚴(yán)重。代碼里面夾雜了大量的
HTML Text
。分?jǐn)?shù)自然是
0
。
JSP, Freemarker, Velocity
都能夠獲得
10
分。用戶后臺代碼十分純凈,不需要引入具體框架的代碼。任何一份
Action
Code
,完全不用知道自己使用的是什么
Template
,這三種
Scripted Template
都能夠隨意替換。能夠獲得
10
分。
pojo
Taglib
根據(jù)各種具體情況,能夠最高獲得
8
分。
Fastm, DOMPlus
需要根據(jù)一定的約定,產(chǎn)生
POJO
數(shù)據(jù)。用戶
Action Code
同樣不需要引入具體的框架代碼,產(chǎn)生的這些數(shù)據(jù)同樣可以很容易地被其他
Template
,比如
JSP, Freemarker, Velocity
使用,能夠某種程度上替換
Template
。能夠獲得
6
分。
Tapestry
需要在每份用戶
Action Code
里面引入
Template
框架的
Package
。只能獲得
4
分。
Wicket
不僅需要在每份用戶
Action Code
里面引入框架的
Package
,還需要引入框架特殊的
View Data Model
數(shù)據(jù)類型,并且提供特殊類型的數(shù)據(jù)。只能獲得
2
分。
XMLC, Rife, JDynamiTe
不僅需要在每份用戶
Action Code
里面引入框架的
Package
,而且需要大量的
Template
操作。只能獲得
0
分。
?
(這項(xiàng)特性的比較,對于
Tapestry
,
Wicket
來說是不公平的。因?yàn)樗鼈兊目蚣芫桶?/span>
Template
本身。
Action
里面引入框架
Package
是很正常的。而且這些框架同樣可以外接其余的
Template
,只是原來的編程模型,需要做一些更改。這里只是對于單項(xiàng)比較就事論事。)
(7) Infrastructure Code Purity
基架代碼純凈度
這里是指框架的內(nèi)部實(shí)現(xiàn)代碼里面是否夾雜了
HTML Text
污染。這也意味著如果用戶需要擴(kuò)展頁面
UI
組件,是否也需要在代碼里面夾雜
HTML Text
。
HTML Taglib, Wicket, Tapestry
的框架實(shí)現(xiàn)代碼里面包含了很多
HTML Text
輸出語句。用戶需要自定義擴(kuò)展頁面
UI
組件,也需要在代碼里面夾雜
HTML Text
。所以,得分只能是
0
。
JSP, Freemarker, Velocity, XMLC, XSL, Rife,
JDynamiTe, Fastm, DOMPlus
得分都是
10
。
(8)
動態(tài)
Include
即運(yùn)行的時候,動態(tài)選擇
Include
另外的
Template
文件。
JSP
文件里面的
@ include
屬于靜態(tài)
Copy And Paste
技術(shù)。
Jsp:include
命令是動態(tài)
Include 相當(dāng)于
<%?
request.getRequestDispatcher(…).include(request, response);
%>
這才是動態(tài)
Include
技術(shù)。
?
Velocity, Freemarker
的
#Parse
指令應(yīng)該也是動態(tài)解釋執(zhí)行的。也可以算是動態(tài)
Include
。
至于
XMLC, Rife,
JDynamiTe
這類技術(shù)能夠隨意操作
Template
Node
,動態(tài)
Include
也是小菜一碟。
Fastm, DOMPLus
同樣提供了操作
Template Node
的能力,而且為了避免這類
Template Manipulation
代碼污染,還提供了類似于
XSL
的
Node Interceptor
的機(jī)制實(shí)現(xiàn)動態(tài)
Include
。
XSL Apply Imports Call Template
能夠動態(tài)引入并使用其他的
XSL
。
?
所以,
JSP,
Freemarker, XMLC, Rife, JDynamiTe, Fastm, DOMPlus, XSL
的動態(tài)
Include
方面的分?jǐn)?shù)都是
10
。
其余的,
Taglib,
Wicket, Tapestry
得分為
0
。
(9)Recursive Display of Tree Data
樹型數(shù)據(jù)的遞歸顯示
遞歸顯示一個任意深度的樹型數(shù)據(jù),這是一個動態(tài)
Include
基礎(chǔ)上的更高級的需求。可以說,不支持動態(tài)
Include
,就不支持遞歸顯示。
遞歸,
XSL
無疑是天生贏家。
XSL
的
Pattern Match
語法可以說就是為遞歸編寫的。
其余的語法都是
Imperative
Programming
。遞歸的前提是必須能夠定義一個方法,自己直接或者轉(zhuǎn)彎抹角的能夠調(diào)用到自己。
對于
JSP,
Velocity, Freemarker
這類沒頭沒尾的
Script
來說,屬于強(qiáng)人所難。
Tapestry, Taglib, Wicket
比較牛,專門提供了
Tree Model
。
XMLC, Rife, JDynamiTe
這些
Template Manipulator
高興了,可以在
Java
代碼里面任意根據(jù)數(shù)據(jù)任意操作
Template Node
。
Fastm, DOMPlus
不僅可以在
Java
代碼里面任意操作,而且提供了類似于
XSL Pattern Match
的
Node Interceptor
功能,不需要寫
Template Node
操作代碼,就可以實(shí)現(xiàn)遞歸。而且可以實(shí)現(xiàn)
Data Iterator +
Template Iterator
的匹配序列。
?
遞歸方面,得分如下。
XSL, XMLC, Rife, JDynamiTe, Fastm, DOMPlus
得分
10
。
Tapestry, Taglib, Wicket
能夠顯示特定的
Tree Model
。得分
4
。
其余的,得分
0
。只能通過
Java
代碼里面夾雜一堆的
HTML Text
,然后整體輸出給
Scripted Template
來實(shí)現(xiàn)。
(10) Space Efficiency
空間效率
基于
Template
Manipulation
的技術(shù)都有空間效率問題。用戶同時訪問同一個
Page
的時候,內(nèi)存中存在多個副本。
XMLC
的問題可能最重。因?yàn)?/span>
XML DOM
結(jié)構(gòu)很重。
JDynamicTe, Rife
直接在一個
Template Node
上操作,如果有多個用戶同時訪問同一個
Page
。那么同一份
Template Node
就會在內(nèi)存中
Duplicate
多份。
?
空間效率方面得分情況
XMLC
得分
0
。
JDynamicTe,
Rife
得分
3
。如果靜態(tài)文本節(jié)點(diǎn)作了優(yōu)化,分?jǐn)?shù)可能更高。
Taglib
由于編譯的結(jié)果非常臃腫,
Tag
之間的信息交流非常困難。分?jǐn)?shù)為
6
。
DOMPlus
一份
DOM
產(chǎn)生多份
SAX Event
,沒有嚴(yán)重的多副本問題,但是
DOM
結(jié)構(gòu)本身比較大,所以得分為
6
。
其余的技術(shù),內(nèi)存里的靜態(tài)文本都只保存一份,都沒有嚴(yán)重的空間效率問題,得分都是
10
。
(11) Mapping Explicitness
映射關(guān)系明顯度
什么數(shù)據(jù)應(yīng)該顯示在什么位置,一目了然。這種特性。
JSP, Velocity, Freemarker
直接在
Template
里面把數(shù)據(jù)取出來顯示,一目了然,清清楚楚,得分都是
10
。
Wicket
的強(qiáng)制
View Model
類型這里幫了大忙,無時無刻不提醒用戶
Model
和
View
(Template)
之間的映射關(guān)系。得分
8
。
XMLC
直接操作
HTML Node By ID, or By Generated Method,
得分為
7
。
比起,
JSP
等來說,
Taglib
的映射關(guān)系就隔了一層。尤其是當(dāng)
Tag
之間存在層次關(guān)系的時候,比如,
Form Tag
下面的
Input Tag
,
Select Tag
下面的
Option Tag
。
Taglib
的分?jǐn)?shù)只有
6
。
XSL
的
XPath Pattern Match
也是要稍微轉(zhuǎn)個彎,類似于
AOP Interceptor
的思路。得分為
5
。
Tapestry
的配置如此復(fù)雜,得分只有
4
。
Rife, JDynamicTe
直接操作
Template Node
,而且是自定義層次的
Template Node
,用戶編寫
Action Code
的時候,必須隨時查看
Template
里面的那些自定義標(biāo)簽之間的層次關(guān)系,并完全理解,了然于胸,才可能編寫正確的代碼。這方面的成本大大提高。分?jǐn)?shù)只有
3
。
Fastm, DOMPlus
的問題類似,也是自定義層次的
Template Node
,需要隨時查看
Template
里面的那些自定義標(biāo)簽(或者
DOM Attribute
)之間的層次關(guān)系。分?jǐn)?shù)只有
3
。
?(12) Display Logic
Reusability
顯示邏輯重用度
嵌在
Template
里面的
Server Side Script
代碼,不具有任何可重用性。除了整個
Include
,你無法在另外的地方調(diào)用
Template
里面的某一段代碼。
JSP, Velocity, Freemarker, Logic Taglib,
Tapestry Logic Tag
,
XSL
的邏輯可重用度分?jǐn)?shù)都是
0
。當(dāng)頁面設(shè)計(jì)人員更改了具體頁面布局元素(
HTML Tag
)的時候,原來的
Template
里面的
Script
全部作廢,需要重新填充到新的
HTML
里面。
Template Manipulation
和
Model Match
技術(shù)的顯示邏輯都存在后臺的
Java
代碼里面,自然是可以重用的。方法調(diào)用,類繼承,包含,怎么都行。
?
Wicket
的
View Model
都是綁定到具體的
HTML UI Tag
上,比如,
List, Table
等。當(dāng)這些
Tag
變化較大的時候,原有的代碼都需要改變。某些
HTML Display Taglib
也是如此。重用度分?jǐn)?shù)為
4
。
當(dāng)結(jié)構(gòu)層次沒有變化,只是具體的
HTML Tag
變化的時候,
XMLC
的原有
DOM
處理代碼幾乎不需要變動。在處理循環(huán)的時候,代碼需要
Create
Specific HTML DOM Node
,然后添加到某個
DOM Node
上面。而且代碼可能大量使用自動產(chǎn)生的代碼的方法。這影響了它的得分,分?jǐn)?shù)為
4
。
當(dāng)結(jié)構(gòu)層次沒有變化,只是具體的
HTML
布局元素發(fā)生了變化,那么,
Rife,
JDynamiTe,
的代碼都不需要變化。但是,它們的代碼侵入性非常強(qiáng),比
XMLC
還要強(qiáng)(如果
XMLC
采用標(biāo)準(zhǔn)的
HTML DOM
操作方法)。權(quán)衡考慮,
Rife, JDynamiTe
的重用度分?jǐn)?shù)是
5
。
當(dāng)結(jié)構(gòu)層次沒有變化,只是具體的
HTML
布局元素發(fā)生了變化,
Fastm,
DOMPlus
的代碼也不需要變化。而且,
Fastm,
DOMPlus
沒有代碼侵入性,產(chǎn)生的
Data
Model
就是
POJO
,可以用在
JSP, Velocity, Freemarker
,
Taglib
里面。所以,重用度分?jǐn)?shù)為
8
。
Scripted Template
前面講述了評估指標(biāo)。下面分別各項(xiàng)技術(shù)進(jìn)行單項(xiàng)說明。
(1) Scripted Template
HTML
和
Server Side Script
混雜在一起的顯示層技術(shù)。
包括
JSP,
Velocity, Freemarker, Taglib, Tapestry, XSL
等。
?
Server Side
的這些
Scripted Template
技術(shù)比較流行,耳聞能詳。前面進(jìn)行指標(biāo)描述的時候,各種參數(shù),也基本上涉及到了。就不具體展開進(jìn)行單項(xiàng)的用法說明和特性分析。
JSP, Velocity, Freemarker
的優(yōu)勢在于這些技術(shù)對用戶后臺
Java
代碼侵入性非常低,這些
Template
都可以任意替換,而不影響用戶后臺
Java
代碼。
?
下面講述另外兩類不是很常見的技術(shù)。
(2)Template Manipulation
Java
代碼直接操作
Template
(比如,
HTML DOM
)產(chǎn)生結(jié)果的顯示層技術(shù)。
包括
XMLC,
JDynamiTe, Rife
等。
?
(3) Model Match
Java
代碼負(fù)責(zé)提供符合顯示層要求的
Data Model
,顯示層框架本身把
Data Model
和
Template
進(jìn)行匹配,產(chǎn)生結(jié)果。
包括
Wicket,
Fastm, DOMPlus,
等。
Template Manipulation
Java
代碼直接操作
Template
(比如,
HTML DOM
)產(chǎn)生結(jié)果的顯示層技術(shù)。
包括
XMLC,
JDynamiTe, Rife
等。
這類技術(shù)都具有良好的所見即所得特性。
(1)XMLC
http://xmlc.enhydra.org/
XMLC
把一個
HTML
文件翻譯成一個
Java
HTML DOM Class
,
比如,
<INPUT ID="nameInput">
<TITLE id="title">Hello,
World</TITLE>
<SPAN id="para1">...</SPAN>?...
這些具有
id
的
HTML
元素,在
Java HTML DOM Class
都產(chǎn)生了對應(yīng)的方法。
HTMLElement getElementPara1()
public void setTextPara1(String text)
HTMLTitleElement getElementTitle()
HTMLInputElement getElementNameInput();
?
比如,
<INPUT
NAME="myName"> CLASS="class1 class2">
就產(chǎn)生了如下的
Constant
Fields.
??
public static final String NAME_myName;
??
public static final String CLASS_class1;
??
public static final String CLASS_class2;
?
具體操作代碼如下,
HTMLObject htmlObj = new HelloHTML();
// Construct head
HTMLHeadingElement head =
htmlObj.createElement("h1");
Text headText =
htmlObj.createText("Hello World");
head.appendChild(htmlTest);
?
// Construct anchor
HTMLAnchorElement anchor =
htmlObj.createElement("a");
anchor.setHref("Welcome.po");
Text anchorText =
htmlObj.createText("Welcome Page");
anchor.appendChild(anchorText);
?
// Replace contents of id-labeled node.
Element replace = htmlObj.getElementReplaceme();
Element parent = replace.getParent();
?
// Start with the last new child so we can
use insertBefore
parent.replaceChild(anchor, replace);
parent.insertBefore(head, anchor);
?
可以看到,用戶的
Action
Code
里面充滿了
HTML DOM
Node
的添加刪除操作。而且里面使用的代碼都不是標(biāo)準(zhǔn)的
DOM
操作方法,而是代碼生成的方法。代碼侵入性非常強(qiáng),如果要換成別的
Template
,比如
JSP, velocity
所有的代碼都要作廢。
當(dāng)然
XMLC
產(chǎn)生的是一個
DOM
,后面還是可以接續(xù)
XSL
的。
?
一般來說,
XML DOM
操作只能針對完整的
Node
。一般需要替換整個
Attribute
,整個
Text
。
對于,
<a href=”http://www.mydomain.com/mymodule/{id}.html”
這類只需要替換某一部分的
Attribute
來說,處理起來就有點(diǎn)大而無當(dāng)。這時候,
XMLC
引入了外部的
Regular Expression Matcher
等工具來處理這種情況。
另外有一個不常見的需求。動態(tài)替換
Java Script
代碼的里面的某一部分。這時候,
XMLC
就完全無能為力了。或許也可以引入外來的
Text Parser Engine
,比如
Velocity, Freemarker, Fastm, JDynamicTe
等來做這件事情。
?
XMLC
的主要問題還是空間效率問題。每次請求,用戶需要產(chǎn)生一個
Java DOM Class
副本,進(jìn)行操作。如果有多個用戶訪問同一個
Page
,那么就同時存在多個
Java
DOM Class
副本。
當(dāng)然里面的靜態(tài)文本資源是共享的,我們看到上面的
Java DOM Class
里面,產(chǎn)生了很多
String
常數(shù)。
但是
DOM Node
結(jié)構(gòu)本身的尺寸就比較大。即使采用了一些優(yōu)化簡化的
DOM Parser
,去除了用不到的結(jié)構(gòu),整個尺寸還是比較大。
(2) JDynamiTe
http://jdynamite.sourceforge.net/doc/jdynamite.html
?
JDynamiTe
是
PHPLib Template
的移植。采用
XML Comment
的方式標(biāo)記動態(tài)結(jié)構(gòu)塊。
我們來看一個典型的兩層循環(huán)的例子。
<tr><td>
?
<table border=1>
<!-- BEGIN DYNAMIC : myBigRow -->
<tr>
<!-- BEGIN DYNAMIC : colX -->
<td>{VALUE_X}</td>
<!-- END DYNAMIC : colX -->
<!-- BEGIN DYNAMIC : colY -->
<td bgcolor="#33CCFF">{VALUE_Y}</td>
<!-- END DYNAMIC : colY -->
</tr>
<!-- END DYNAMIC : myBigRow -->
</table>
?
對應(yīng)的代碼是,
import cb.jdynamite.*;
?
dynamiTe=new JDynamiTe();
dynamiTe.setInput(application.getRealPath("cbtemplate/testTemplate.html"))
??? // Second table with
nested Dynamic Element
??? for (int row = 0; row < 5; row++) {
??????? // first group of columns
??????? // 4) Use
"setDynElemValue" to set or reset the value of a Dynamic Element
???????
dynamiTe.setDynElemValue("colX", ""); // reset for each row
??????? for (int col = 0; col < 3; col++)
{
???????????
dynamiTe.setVariable("VALUE_X", "line_" + row +
",col_" + col);
???????????
dynamiTe.parseDynElem("colX"); // add a column
??????? }
??????? // second group of columns
???????
dynamiTe.setDynElemValue("colY", ""); // reset for each row
??????? for (int col = 3; col < 5; col++)
{
???????????
dynamiTe.setVariable("VALUE_Y", "line_" + row +
",col(BIS)_" + col);
???????????
dynamiTe.parseDynElem("colY"); // add a column
??????? }
???????
dynamiTe.parseDynElem("myBigRow"); // add a row
??? }
??? // 5) Use
"parse" to finaly get the value of your Dynamic Template Document
??? dynamiTe.parse();
??? out.println(dynamiTe.toString());
// send HTML page
?
我們看到,
Template
本身操作貫穿程序的始終。
setDynElemValue, setVariable, parseDynElem,
parse
都是
template
Class
本身的方法,類似于
XML
DOM Node
的添加刪除修改。
我們看到這類
DOM
Manipulator
的代碼侵入性非常強(qiáng),用了之后,如果要換別的
Template
,比如
JSP, velocity
,這段代碼完全作廢。
(3) Rife
http://rifers.org/
類似于
JDynamiTe
,
Rife
也采用自定義動態(tài)塊標(biāo)簽。
下面是一個典型的例子。遞歸顯示一個
Data Tree
。下面只是核心片斷。如果對整個例子感興趣,可以去
Rife
的網(wǎng)站查看
Sample
和
Tutorial
。
這是一段
Tree
Template
。
<body>
${v level/}
${b level}
?
<ul>${v nodes/}</ul>
${/b}
${b node}
?
<li>${v title/}${v level/}</li>
${/b}
</body>
?
對應(yīng)的
Java
代碼操作
Template Node,
輸出
Data Tree
。
?
import com.uwyn.rife.engine.Element;
import
com.uwyn.rife.template.InternalValue;
import com.uwyn.rife.template.Template;
?
// obtain an instance of the template that
will output the tree
Template template =
getHtmlTemplate("tutorial.recursion");
…
// obtain a new internal value to construct
a collection
// of sibling child nodes in the local
scope
InternalValue?? nodes = template.createInternalValue();
…
// set the child's title value
template.setValue("title",
encodeHtml(child.getTitle()));
// and append it to the local internal
value
nodes.appendBlock("node");
…
// set the level value which includes the
sibling nodes in the
// same level
template.setBlock("level",
"level");
?
我們看到,
template
的操作代碼貫穿整個程序的始終。
getHtmlTemplate, createInternalValue,
setValue, appendBlock, setBlock
。非常類似于上面
JDynamiTe
的用法。
JDynamiTe
顯示
Data Tree
的過程也是大致如此。
XMLC
也是如此。
Rife
同樣具有
JDynamiTe
和
XMLC
的代碼侵入性強(qiáng)的缺點(diǎn)。如果需要換別的
Template
技術(shù),比如
JSP, velocity
,整個代碼都要做廢。
Model Match
Java
代碼負(fù)責(zé)提供符合顯示層要求的
Data Model
,顯示層框架本身把
Data Model
和
Template
進(jìn)行匹配,產(chǎn)生結(jié)果。
包括
Wicket,
Fastm, DOMPlus,
等。
這類技術(shù)都具有良好的所見即所得特性。
(1) Wicket
http://wicket.sourceforge.net/
Wicket
類似于
Tapstry
,采用
HTML
自定義
Attribute
作為自定義標(biāo)簽。
這段是
Rife
的一個典型的循環(huán)的例子。
wicket:id
一個標(biāo)簽,幾乎可以滿足任何需求。有興趣的讀者可以去
Wicket
網(wǎng)站查看完整的
Sample
。這里只有核心片斷。畢竟,本文不是一部
Wicket
教程。
?
<html>
<body>
?
<form wicket:id = "commentForm">
???
Add your comment here:
???
<p>
???
<textarea wicket:id = "text">This is a
comment</textarea>
???
<p>
???
<input type = "submit" value = "Submit"/>
?
</form>
?
<p>
?
<span wicket:id = "comments">
???
<p>
??????
??? <span wicket:id = "date">1/1/2004</span><br>
??????
??? <span wicket:id =
"text">Comment text goes here.</span>
?????? </p>
?
</span>
?
<wicket:remove>
???
<p>
??????
???
1/2/2004<br/>
??????
??? More comment text here.
???
</p>
?
</wicket:remove>
</body>
</html>???????????????????????
?
我們可以看到,
Template
非常干凈。只有少數(shù)的自定義
attribute, (and tag)
。
對應(yīng)的
Java
代碼如下。
?
import wicket.markup.html.WebPage;
import wicket.markup.html.basic.Label;
import wicket.markup.html.basic.MultiLineLabel;
import wicket.markup.html.form.Form;
import wicket.markup.html.form.TextArea;
import wicket.markup.html.list.ListItem;
import wicket.markup.html.list.ListView;
import wicket.model.PropertyModel;
?
public final class GuestBook extends
WebPage
{
?????? /**
Use a Vector, as it is synchronized. */
?????? private
static final List commentList = new Vector();
?????? private
final ListView commentListView;
?
?????? public
GuestBook()
?????? {
????????????? add(new
CommentForm("commentForm"));
????????????? add(commentListView
= new ListView("comments", commentList)
????????????? {
???????????????????? public
void populateItem(final ListItem listItem)
???????????????????? {
??????????????????????????? final
Comment comment = (Comment)listItem.getModelObject();
??????????????????????????? listItem.add(new
Label("date", comment.date.toString()));
??????????????????????????? listItem.add(new
MultiLineLabel("text", comment.text));
???????????????????? }
????????????? });
?????? }
}
?
我們看到,
Wicket
的代碼,相當(dāng)干凈利索,雖然寫法上使用了匿名內(nèi)部類。沒有任何
Template
本身的操作。只是需要提供一個框架需要的
View Model
。
ListView
,
MultiLineLabel
,
Label
。
Wicket
的
PropertyModel
能夠用來包裝一個
POJO
。比如,一段
HTML
Template
Stock of IBM: <span
wicket:id="stockIBM">some value</span>
?
對應(yīng)的
Java
代碼是
import wicket.model.PropertyModel;
?
public PojoStockQuotePage()
{
???
StockQuote quote = new StockQuote("IBM");
???
add(new Label("stockIBM", new PropertyModel(quote,
"quote"));
}
?
我們看到,
Wicket
的代碼結(jié)構(gòu)非常像
Swing
。只需要對應(yīng)
HTML UI Tag
提供一份
View Model
就可以。操作起來實(shí)在是方便。而且
HTML Tag
里面只需要添加
Wicket:id
這樣的自定義
Attribute
,就可以同時表達(dá)動態(tài)層次塊和變量部分(其實(shí)
Rife
也是如此)。
?
當(dāng)需要換
Template
的時候,比如
JSP, Velocity, Freemarker
,
Taglib
等,
Wicket
提供的
View Model
還是可以使用的。
Wicket
的一個不足之處是,代碼需要使用框架自定義的
HTML View Model
。這也可能是一個優(yōu)點(diǎn),能夠幫助用戶清楚地理解,代碼和
HTML Template
之間的對應(yīng)關(guān)系。
?
從嚴(yán)格意義上來說,比起
Taglib,
Tapestry
來說
,
只有
Wicket, Echo
這樣的框架才是真正意義上的組件框架。而且,
Wicket
相對于
Echo
的優(yōu)勢如此明顯,這里就不多說了。不然就跑題了。總之,
Wicket
是一個非常值得關(guān)注的框架。
(2) Fastm
http://fastm.dev.java.net/servlets/ProjectDocumentList
Fastm
的思路相當(dāng)于
JDynamiTe, Wicket
的思路組合。
Fastm = JDynamiTe + Wicket
Fastm
采用自定義標(biāo)簽來標(biāo)記動態(tài)
Block
,然后類似于
JSTL,
Velocity, Freemarker, Tapestry
那樣,接受一個
POJO
作為
Model
,并采用
OGNL Style(
同時也接受
XPath Style)
的方式對數(shù)據(jù)進(jìn)行尋址。
Fastm
的公式很簡單,
Fastm Template + Model = Result
。
這個
Model
是
POJO
。可以是
Java Bean, Map, DOM Node
等任何
Object
。
?
我們來看一段典型的
Tree
Data
遞歸顯示的例子。
這段是
HTML
Template
片斷。
?
class name: <input type="text"
value="{name}">
<!-- BEGIN DYNAMIC: @children -->
<ul>
<!-- BEGIN DYNAMIC: children -->
?
<li>
???
class name: <input type="text" value="{name}">
???
<!-- BEGIN DYNAMIC: @children -->
???
<ul>
???
<!-- BEGIN DYNAMIC: children -->
???
<!-- END DYNAMIC: children -->
???
</ul>
???
<!-- END DYNAMIC: @children -->
?
</li>
<!-- END DYNAMIC: children -->
</ul>
<!-- END DYNAMIC: @children -->
?
上面的
@children
需要特別說明一下,意思是檢測當(dāng)前
Model
是否具有
children
這個
property
,如果具有,那么向下展開,否則就跳過去。這樣的話,如果沒有
children
的話,多余的
<ul> tag
就不需要打印出來了。雖然空
<ul>tag
并不影響顯示。
?
對應(yīng)的
Java
代碼只需要提供一個
POJO
作為
Tree Data
。
?
???
public
Object makeModel(){
?????? Map a =
new
HashMap();
?????? a.put(
"name"
,
"A"
);
?????? List aChildren =
new
ArrayList();
?????? a.put(
"children"
, aChildren);
?????? {
?????????? Map a1 =
new
HashMap();
?????????? a1.put(
"name"
,
"A1"
);
?????????? List a1Children =
new
ArrayList();
?????????? a1.put(
"children"
, a1Children);
?????????? {
????????????? Map a11 =
new
HashMap();
????????????? a11.put(
"name"
,
"A1-1"
);
????????????? a1Children.add(a11);
?
????????????? Map a12 =
new
HashMap();
????????????? a12.put(
"name"
,
"A1-2"
);
????????????? a1Children.add(a12);
?????????? }
?????????? aChildren.add(a1);
?????? }
??????
return
a;
??? }
?
這段代碼采用了
Map
作為
Model,
也可以采用
Java Bean, XML DOM Node
等任何
Object
。所以,當(dāng)然可以提供一個
XML
文件作為
Model
。
?
看起來
Fastm
和
JSP, Freemarker, Velocity
,
JSTL
一樣,對
View Model
沒有什么特殊要求。
POJO
就可以。基本上就是如此。
且慢,
Fastm
對
Model
還是有特殊要求的。類似于
Wicket
,
Fastm
也沒有邏輯標(biāo)簽,
Fastm
也利用數(shù)據(jù)來表示條件、循環(huán)等邏輯分支。遇到
Collection,
Array
等數(shù)據(jù)類型,就自動把動態(tài)塊展開。如果不顯示某一塊,那么就提供一個空數(shù)據(jù)。
這就是
Fastm
所需要的所有約定。看起來很簡單,真正滿足這個約定也不難。但是,某些特殊的情況下,為了滿足這個約定,需要后臺的用戶代碼做一些比
JSP, Velocity, Freemarker
要求的更多的
Model
組裝工作。
Fastm
的另一個問題是,
Model
和
View
(
Template
)之間的映射關(guān)系不是很明了。雖然比
JDynamiTe
,
Rife
等容易明白多了,但是比
Wicket
還是差一些。因?yàn)?/span>
Fastm
沒有自定義
View Model
類型,需要用戶自己掌握
Bean, Map
層次和
Template
層次之間的正確對應(yīng)。
(3) DOMPlus
http://fastm.dev.java.net/servlets/ProjectDocumentList
DOMPlus
是
Fastm
的思路在
XML DOM
領(lǐng)域的擴(kuò)展。
如果說,
Fastm =
JDynamiTe + Wicket
;那么,
DOMPlus = XMLC + Wicket
DOMPlus
是
XMLC
和
Wicket
思路的組合。
如果說,
Fastm
的公式是,
Fastm Template + Model = Result
。
那么,
DOMPlus
的公式是,
DOM + Model = DOM or SAX
這個
Model
是
POJO
。可以是
Java Bean, Map, DOM Node
等任何
Object
。
一個很有趣的現(xiàn)象就是
DOM
+ DOM = DOM
沒錯,就是如此。
?
DOMPlus
采用自定義
DOM Attribute
來標(biāo)記動態(tài)
Element,
動態(tài)
Attribute,
動態(tài)
Text
。
我們來看一個典型的
Tree
Data
遞歸顯示的例子。
對應(yīng)的
HTML DOM
片斷是。
?
?
<li nodeTarget = "child">
???
class name: <input type="text" attributesTarget="value=@name"
/>
???
<ul>
???
<li nodeTarget = "child" />
???
</ul>
?
</li>
?
nodeTarget
表示可能被重復(fù)多次的動態(tài)
Element
,
attributesTarget
表示需要替換的
attribute
。這里數(shù)據(jù)尋址方式采用的是
XPath
。
@name
表示
DOM Node
的
name attribute
。
DOMPlus
的自定義標(biāo)簽只有
3
個,
nodeTarget,
attributeTarget, textTarget
。
?
對應(yīng)的
XML Tree
Data
是,
?
<data name="topClass">
<child name="A">
?
<child name="A-1">
???
<child name="A-1-1"/>
?
</child>
?
<child name="A-2">
???
<child name="A-2-2">
?????
<child name="A-2-2-3" />
???
</child>
?
</child>
?
<child name="A-3"/>
</child>
<child name="B">
?
<child name="B-1" />
</child>
<child name="C" />
</data>
?
這兩個
DOM
一匹配,就產(chǎn)生了結(jié)果
XML
,是一棵顯示在
HMTL List
里面
Tree
。
?
DOMPlus
的
Template
更加干凈,幾乎接近于
XMLC
的
Pure HTML DOM
,等同于
Wicket
。
而且
DOMPlus
并沒有
XMLC
的空間問題。
DOMPlus
只保留一份
DOM
在內(nèi)存中,每次請求來的時候,
DOMPlus
根據(jù)數(shù)據(jù)匹配產(chǎn)生
SAX Event
,直接寫入
Response
。
?
DOMPlus
的匹配引擎很類似于
XSL
的功能,同樣是用
XML
格式的模板文件處理
XML
數(shù)據(jù)。而且兩者都同樣是遞歸處理引擎。
所不同的是
DOMPlus
Template
能夠在瀏覽器中正確顯示,而且表達(dá)結(jié)構(gòu)的自定義屬性非常簡單,只有
3
個(
Fastm
有
2
個)。
?
DOMPlus
的問題和
Fastm
一樣,數(shù)據(jù)層次和模板層次之間的關(guān)系,一定要非常清楚,而不是像
JSP, Velocity, Freemarker
那樣把數(shù)據(jù)抓過來就可以用。
另外的問題就是,處理的最小單位是
XML Node
。
XML Node
里面的
text
的部分替換就無能為力了。比如
<a href=”http://www.domain.com/module/{id}.html”
。
和
XMLC
的解決方法一樣,可以引入外面的文本解析器。用來處理
XML Node
鞭長莫及的地方,比如,
JavaScript
代碼的內(nèi)部的動態(tài)替換部分。
Regular Expression
,
Velocity, Freemarker, Fastm,
JDydanamiTe
,等任何能夠脫離
Web
環(huán)境的通用文本解析工具都可以。
JSP, Taglib,
Tapestry, Wicket
等無法脫離
Web
環(huán)境而存在,肯定不行。
?
------
注:
Fastm
和
DOMPlus
是我的作品,輕量的
Template
匹配引擎。這兩項(xiàng)技術(shù)本身只是單項(xiàng)的
Template
顯示技術(shù),不是一個完整的
web
整體解決方案。
特性總表
(1) Host Language Consistency
宿主語言一致性
(2)Template Purity
模板純凈度
(3)Template Tidiness
模板整潔度
(4) Replacement Flexibility
替換靈活度
(5)WYIWYG
所見即所得
(6)Action Code Purity
用戶代碼純凈度
(7) Infrastructure Code Purity
基架代碼純凈度
(8)
動態(tài)
Include
(9)Recursive Display of Tree Data
樹型數(shù)據(jù)的遞歸顯示
(10) Space Efficiency
空間效率
(11) Mapping Explicitness
映射關(guān)系明顯度
(12) Display Logic Reusability
顯示邏輯重用度
?
限于空間,下面的分?jǐn)?shù)表采用索引數(shù)字來代表特性。橫向是特性,縱向是技術(shù)。
下面的表格中沒有寫
Freemarker
,只寫了
Velocity
,這兩項(xiàng)技術(shù)比較類似。各項(xiàng)參數(shù)也大致接近。當(dāng)然,各自的
Fans
都能夠看到很深很細(xì)的使用細(xì)節(jié),宏定義之類的。
?
指標(biāo)索引
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
11
|
12
|
Scripted Template
|
Velocity
|
2
|
0
|
0
|
10
|
0-5
|
10
|
10
|
10
|
0
|
10
|
10
|
0
|
JSP
|
10
|
0
|
0
|
10
|
0
|
10
|
10
|
10
|
0
|
10
|
10
|
0
|
Taglib
|
0
|
0
|
10
|
6
|
0-5
|
2 - 8
|
0
|
0
|
4
|
6
|
6
|
0
|
Tapestry
|
0
|
0
|
10
|
6
|
7
|
4
|
0
|
0
|
4
|
10
|
4
|
0
|
XSL
|
10
|
0
|
10
|
6
|
0
|
10
|
10
|
10
|
10
|
10
|
5
|
0
|
Template Manipulation
|
XMLC
|
5
|
10
|
10
|
6
|
10
|
0
|
10
|
10
|
10
|
0
|
7
|
4
|
JDynamiTe
|
5
|
8
|
5
|
10
|
8
|
0
|
10
|
10
|
10
|
3
|
3
|
5
|
Rife
|
5
|
3-7
|
5
|
10
|
0-7
|
0
|
10
|
10
|
10
|
3
|
3
|
5
|
Model Match
|
Wicket
|
5
|
9
|
8
|
6
|
9
|
2
|
0
|
0
|
4
|
10
|
8
|
4
|
Fastm
|
5
|
7
|
5
|
10
|
8
|
6
|
10
|
10
|
10
|
10
|
3
|
8
|
DOMPlus
|
5
|
9
|
8
|
6
|
10
|
6
|
10
|
10
|
10
|
6
|
3
|
8
|
?
?
下面我們大致看一下
Browser
Side
(
Ajax
)的情況。
Browser Side
同
Server Side
的技術(shù)格局恰好相反,
Browser Side
的
HTML DOM Manipulation
技術(shù)、
HTML View Model
技術(shù)比較多,比較流行。各種
JavaScript UI
控件,
DOM
綁定控件,這方面的庫很多,而且很酷。這里不再列舉了。
?
Browser Side
的
Scripted Template
技術(shù)就比較少見了。我知道的大概有下面幾個。
?
TrimPath
的
JST
http://www.trimpath.com/project/wiki/JavaScriptTemplates
語法舉例
<option
value="${country.name}" {if country.name == currCountry}selected{/if}>
?
?
Helma
http://dev.helma.org/Wiki/JavaScript+Template+Engine/
語法舉例
?
<% if (session.user != null) %>
???
Hello <%= session.user.name %>
?
<% else %>
???
<a href="/login">Login</a>
?
<% end %>
?
SWATO
http://swik.net/SWATO/Swato+JavaScript+Template
語法舉例
#for (i in products) {
#???
var p=products[i];
???
<td>$<%p.price%></td><td><%p.quantity%> :
<%p.alert%></td>
???
</tr>
#}
?
以數(shù)據(jù)為中心的
Ajax
應(yīng)用中(把數(shù)據(jù)取過來,而不是取一段
HTML
,或者一段
Script
),當(dāng)頁面布局結(jié)構(gòu)比較復(fù)雜的情況下,也可以選擇
Browser Side XSL
。
?
一個有趣的聯(lián)想是
DOMPlus
的思路。
DOMPlus
不僅支持
DOM + DOM = SAX
,而且支持
DOM + DOM = DOM
。
這個特性特別適合于
Browser
Side
。假設(shè)存在
DOMPlus
的
Javascript
版本。
Javascript
從
Server Side
拿來
XML Data
,和
Browser
里面的一段
HTML
進(jìn)行一下
Match
,顯示就搞定了。不需要寫任何代碼。
Unobtrusive
在
Brower Side
方面,
Scripted Template
技術(shù)并不流行。
這個事實(shí)說明了,
Browser
Side
的顯示技術(shù)更加歸于常態(tài),顯示模型更加自然。
JavaScript
編程有個流行的概念,叫做
Unobtrusive
,就是我們常說的無侵入性。
JavaScript, HTML, CSS
清晰有效的分開。
行為的歸行為,內(nèi)容的歸內(nèi)容,風(fēng)格的歸風(fēng)格。
凱撒的歸凱撒,上帝的歸上帝,人民的歸人民。
各自照看好自己的領(lǐng)域,而不侵入他人的領(lǐng)域。
?
無侵入,
POJO
等概念,在
Server Side
方面(比如
Java
),也是甚囂塵上,炒作的不亦樂乎。
但是在
Web
顯示技術(shù)的方面,
Unobtrusive
無侵入特性,不能不說,
Browser Side
由于先天的
JavaScript
操作
DOM
的優(yōu)勢,已經(jīng)走在了前面。
?
雖然
JavaScript
作為一門動態(tài)語言,開發(fā)效率自然超過強(qiáng)類型編譯語言,但是代碼維護(hù)、
IDE
提示、輔助重構(gòu)方面的成本也不可低估。
所以,
Server Side
的顯示技術(shù),仍然是不可缺少的。在
Server Side
同樣應(yīng)用
Unobtrusive
原則,也仍然具有重要的意義。
前面提到的兩個指標(biāo),
Template
Purity
模板純凈度
, Action
Code Purity
用戶代碼純凈度。
就屬于
Unobtrusive
指標(biāo)。
?
顯示技術(shù)里面,代碼邏輯表示動態(tài)部分,復(fù)雜部分;
Template
表達(dá)靜態(tài)部分,簡單部分。
所以,人們更加關(guān)注代碼邏輯的容易管理的程度。
由于代碼邏輯在
IDE
里面相對容易管理。人們更能夠容忍
Java
或者
Java Script
代碼里面出現(xiàn)具體的
Template Node
,而覺得
Template
里面的
Script
比較難以管理。
?
Scripted Template
基本上是
Obtrusive
的,對
Template
的侵入性最強(qiáng)。雖然
Template
操作沒有侵入到
Java
或者
JavaScript
代碼。
這叫做
1 -- Way
Obtrusive
?
Template Manipulation
大致能夠做到對
Template
的
Unobtrusive
非侵入,雖然他們的
Template Node
操作侵入了
Java
或者
Java Script
代碼。
這叫做
1-Way
Unobtrusive
。
?
Model Match
技術(shù)具有最好的
Unobtrusive
非侵入特性。
Java
或者
JavaScript
代碼不侵入
Template
到里面,具體的
Template Node
操作也不侵入到
Java
或者
JavaScript
代碼里面。
這叫做
2-Way
Unobtrusive
。
?
Fastm, DOMPlus
是天生的
Model Match,
具有
2-Way Unobtrusive
特性。
Wicket
也是天生的
Model Match
,大致能夠做到
1.5 -Way Unobtrusive
。
如果嚴(yán)格限制不采用
Logic
Taglib, Tapstry Logic Tag
,那么
Taglib
,
Tapestry
也能夠做到
1.5 – Way Unobtrusive.
顯示邏輯
AOP
這個需求主要包括頁面數(shù)據(jù)類型的統(tǒng)一格式化。
比如,所有類型為
Date
,名字以
Time
結(jié)尾的數(shù)據(jù)(
startTime, endTime
等),都顯示到秒鐘;
Day
結(jié)尾的時間字段
(registerDay, birthDay
等
)
,都顯示到天。
Period, Quarter, Year
結(jié)尾的字段也都有不同的顯示需求。
能夠支持自定義顯示邏輯
AOP
Interceptor
的技術(shù)并不是很多。
XSL
語法天生就是
AOP
語法,
Declaring
,
Pattern Match
,用法就是要求程序員編寫
Interceptor
。
Fastm, DOMPlus
對這方面也支持的很好。同樣是采用自定義
Interceptor
。
?
W3
的
DOM Level 2
規(guī)范定義了
DOM Traversal
。
http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/traversal.html
DocumentTraversal, TreeWalker, NodeIterator,
NodeFilter
。
使用
Pull
模型(
SAX
是
Push
模型)處理
XML
的程序員,可以使用
NodeFilter
來過濾掉不需要顯示的節(jié)點(diǎn)。
NodeFilter
畢竟只是一個
Filter
,只能對
Node
內(nèi)容進(jìn)行簡單的開關(guān)選項(xiàng)處理,
YES, or No
,顯示或者不顯示。只是作為
DocumentTraversal
的
whatToShow
參數(shù)的一個補(bǔ)充。
AOP
能力很有限。
多語言支持的終極解決方案
多語言支持,也叫做國際化,本地化。
一般采用字典文件的做法。比如,
dict.en, dict.cn, dict.fr, dict.de, dict.jp
等。
在這類做法里面,
Template
里面通常都只有
Message Key
。
<span>< message key=”name” /></span>
?
有些更好的做法是這樣,提供缺省文本信息。
<span id=”key.name”>
用戶名稱
</span>
?
這樣能夠保持頁面的一目了然。
?
除了字典文件的做法之外,另一種做法是直接把文字資源,存放到
Template
里面。
然后分多個目錄。
En,
cn, fr, de, jp
等目錄,下面全都是對應(yīng)的
Template
文件。
這種方案叫做語言目錄方案。
這種方案的缺點(diǎn)很明顯,一個文件的改動,要擴(kuò)散到所有的對應(yīng)文件中。
這種方案的優(yōu)點(diǎn)也很明顯,文件內(nèi)容一目了然,尤其是支持所見即所得的模板。另一個優(yōu)點(diǎn)就是運(yùn)行效率。字典文件方案運(yùn)行的時候,需要進(jìn)行大量的查字典,動態(tài)替換文本工作。而語言目錄方案的模板里面大部分都是靜態(tài)文本。
?
有沒有一個兩全其美的方案?答案是,有。
首先,我們采用自己的母語(比如,中文)作為主模板文件,都放在
cn
目錄下。
然后,其中需要多語言的文本信息,都采用下面這種方式包裝起來。
<span id=”key.name”>
用戶名稱
</span>
<p id=”key.help.charpter1”>long text about system help</p>
?
這時候,
Template
仍然保持了所見即所得的特性。然后,我們根據(jù)這些
Key
,做其他語言的字典文件。
Dict.en, dict.jp,
dict.fr, etc.
然后我們用一個文本處理引擎,替換掉這些多語言信息。為每一種語言產(chǎn)生一個目錄,下面都是對應(yīng)的語言的
Template
文件。比如,
en, jp, fr,
等
Template
文件目錄,里面都是對應(yīng)的填充了內(nèi)容的模板。打開一看,一目了然。
當(dāng)然,這個處理過程中,并沒有影響那些動態(tài)數(shù)據(jù)部分,而只是替換了靜態(tài)文本部分。
每次只需要更改主要語言目錄的文件,然后用引擎處理,變化自動分布到其他語言目錄。
這種技術(shù)的關(guān)鍵在于,
Template
本身是否可再生資源?能否被多次處理?能否被統(tǒng)一處理?
有幾種技術(shù)是具有這種可能性的。
Taglib
理論上可以被當(dāng)作
XML
文件處理,具有理論上的可行性。
Fastm, DOMPlus
具有現(xiàn)實(shí)可操作性。
Fastm, DOMPlus
都可以自定義標(biāo)簽,處理動態(tài)部分的同時,也能夠忽略其他動態(tài)部分。
總結(jié)與展望
本文分析了
Web
層顯示技術(shù)的各類指標(biāo)和特性。
本文的討論目前還只是局限于一般的
B/S
結(jié)構(gòu)。
Web Service, SOA
代表了
Web
未來發(fā)展的趨向。數(shù)據(jù)整合,流程整合,各類資源整合。
界面
UI
也不會限于
HTML
一種,
XUL, XAML, SVG, RSS
等也有各自的應(yīng)用。
?
我們都知道
Web
中有一種“盜鏈”的現(xiàn)象。
一個網(wǎng)站,不是通過
Copy
,而是直接通過
Link
引入了其他網(wǎng)站的資源,比如
CSS
,圖片資源等。這樣可以節(jié)省自己的
Server
資源。
有些技術(shù)更狠,能夠抓取別人的頁面內(nèi)容,剪貼拼湊之后顯示在自己的網(wǎng)頁上。
這種情況實(shí)際上是一種偷偷摸摸的不被允許的資源共享實(shí)踐。
?
Web Service
把這種實(shí)踐發(fā)展成了一種商業(yè)模式,資源共享模式。不僅可以共享數(shù)據(jù)和資源,而且可以共享內(nèi)容和服務(wù)(比如
Portlet
)。
比如,
Web Service
Remote Portal
,就是一種內(nèi)容提供模式、服務(wù)提供模式。網(wǎng)站流量不再依靠用戶點(diǎn)擊率來計(jì)算,而是依靠
Web Service
調(diào)用率。
?
我們來看看,能夠共享的資源有哪些。
CSS,
圖片,
JavaScript
等可以直接
Link
過來;數(shù)據(jù)、內(nèi)容可以抓取過來。
其中以
CSS
的共享最為流行。
CSS +
圖片
+
某些文字替換,組成了一個
Theme
(顯示主體),或者
Skin
(表觀)。很多人津津樂道,并孜孜不倦地談?wù)摗?yīng)用、提供各種
Themes,
Skins
。
但是還有一個重要的資源共享沒有得到充分的發(fā)展。就是
Template Layout
的共享。
目前,
Web Server
的
Template
資源,一般都存放在自己的文件系統(tǒng)中。
假設(shè)這樣一種方式。
一個
Web Server
運(yùn)行的時候,通過
Web Service
獲取數(shù)據(jù),通過
Link
引用
CSS
,
JS
,圖片等,通過
XLink +
XPointer + XPath
獲取一份
XML Node
or Fragment or Text
,作為
Template Layout
,自己的服務(wù)器上只需要一份
Display Logic
把這些東西組裝起來,就可以把頁面發(fā)布出來。甚至
Display Logic
也可以從
Web Service
獲取(
Script
等,當(dāng)然這里面涉及到安全問題),自己只負(fù)責(zé)統(tǒng)籌管理安排調(diào)用。
這種模型對于
Web
Service Client
來說,也是適用的。
這種模型的關(guān)鍵就在于,
Unobtrusive
。所有領(lǐng)域都是清楚地分開,
Domain Specific
,決不侵入到其他領(lǐng)域,也不允許其他領(lǐng)域的侵入。
?
以上是我對
Web
顯示技術(shù)的總結(jié)和展望。
本文到這里結(jié)束。