Web顯示層技術評估

名詞界定

顯示層的意思就是Presentation Layer,也翻譯成表現層、展現層、展示層。

本文討論的範圍只包括採用HTML Template的顯示層技術,不包括Echo,GWT(google web toolkit)等根據代碼產生HTML的工具。

本文主要討論Server Side (針對Java Language)的顯示層技術,然後進一步討論Browser Side(Ajax)的顯示層技術(一個典型的Ajax應用也分爲Model, View, Controller – Data, HTML/CSS, JavaScript)。注意,本文關於Ajax的討論只有很少一部分,因爲我不擅長這個領域。只是一個順便的擴展比較。

一個很有趣的現象。Server Side和Browser Side的顯示層技術格局恰好相反。Server Side的Scripted Template技術比較多,比較流行;而Browser Side的HTML DOM Manipulation技術、HTML View Model技術比較多,比較流行。

本文會提到一些技術、或者框架的名稱,但只侷限於討論該技術、該框架的顯示相關部分的內容,而不涉及評估其他方面的特性。比如,本文不討論Link URL Generation, Action URL Generation,Button Script Generation這些頁面組件事件機制的方面。

本文是一個深度討論。不討論簡單的替換個字符串的Hello World案例,而是窮盡各種顯示層技術的能力極限,探索它們在複雜佈局(動態include等)、複雜顯示邏輯(條件、循環、嵌套、遞歸)等方面的功能。

對了,(考慮到Site Mesh,Struts Tiles Taglib等技術廣泛的羣衆基礎),可能需要專門提一下,本文將不討論Site Mesh,Tiles等佈局技術(named include)。

Site Mesh相當於XSL的一個簡化版本,只保留了根據(name->file)配置替換某個HTML Node的能力,其餘的如Tiles,也大致如此,由於多了一個(name->file)配置文件,比直接include file高級了不少。

由於使用簡單(功能自然也簡單),這類技術獲得了廣大羣衆的支持,呼聲很高。本文爲忽略了這一類技術感到很遺憾。

另外需要指出的是,並不存在一個十全十美的方案。

工作總是要做的,不是在Template裏面做,就是在Java Code裏面做,總之,總要找個地方做這個工作,天下沒有免費的午餐。一方面特性增強了,自然影響到另一方面。

正如,代碼的耦合實際上並不能完全消除,我們只能把這些耦合點移動來移動去,今天我看這裏不舒服了,把耦合點移動到另一個地方;明天另一個人看到那裏不舒服了,又移動回來。而且各自都能說出一大堆道理。

所以,需要注意的是,並不存在一個絕對的優勝方案。本文只是列出各種技術指標的參考評估數據,以便幫助讀者根據自己的需要,做出比較準確的評估。(是的,準確的量化評估,而不是廣告語或者口號)

理論模型

一個顯示的整個過程,如果用一個函數來描述,那麼看起來大概是這樣。

F(Data, Template, Display Logic) => Result HTML

其中的Display Logic,就是顯示邏輯。Display Logic操作Data和Template,產生最終結果。

這個Display Logic可能以各種形式出現在任何地點。

比如,可能作爲Server Side Script存在於Template裏面,把Data取出來輸出;也可能存在於後臺Java裏面,根據Data操作Template Node。

針對前一種情況,函數公式表達是:Template Script (Data) => Result

針對後一種情況,函數公式表達是:Logic (Data, Template) => Result

這個模型可以作爲顯示層技術的劃分標準。

(1) Scripted Template

HTML和Server Side Script混雜在一起的顯示層技術。

包括JSP, Velocity, Freemarker, Taglib, Tapestry, XSL等。

肯定有人對這個劃分有異議。XSL裏面有choose, if, for。這還好說。尤其是對Taglib, Tapestry,反映可能更加強烈。我似乎已經看到,Taglib or Tapestry Fans已經跳起來了,高叫着,Taglib or Tapestry明明是組件技術,組件技術,組件技術….

這裏我還是表示很遺憾。在目前定義的這個狹義模型下,任何Template中包含Logic的顯示技術都劃爲Script這一類。而且在表示邏輯的時候,這類組件技術表現的更加突出一些。

比如Tapestry的 等邏輯標籤。尤其是這個if not,是專門多出來的一個條件語句,一般的編程語言裏面都不具備這樣的對應語法。當然,Tapestry並不專美,Taglib的Logic Tag也是如此。

(2)Template Manipulation

Java代碼直接操作Template(比如,HTML DOM)產生結果的顯示層技術。

包括XMLC, JDynamiTe, Rife等。

大家對這一類技術可能不是很熟悉。後面進行特性分析的時候,會舉出一些典型的例子,來說明各自的用法。

一個很有意思的現象是,在Browser Side(Ajax),由於Java Script操作HTML DOM非常方便,這類顯示技術非常普遍。相反的,Scripted Template的技術,在Browser Side卻不多見。後面討論Browser side的時候,會列舉一些典型的例子。

(3) Model Match

Java代碼負責提供符合顯示層要求的Data Model,顯示層框架本身把Data Model和Template進行匹配,產生結果。

包括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。

數據尋址

在正式開始之前,先說明一下數據尋址的概念。

數據尋址,意思是數據訪問、屬性獲取等。主要包括兩類風格。

(1) OGNL Style

http://www.ognl.org/

OGNL (Object Graph Navigation Language)如此著名和深入人心,以至於我在這裏用OGNL Style代表Java Bean屬性尋址的方式。

比如,a.b[1].c.d[2].name

另一類當然是

(2)XPath Style

比如,a/b[1]/c/[d]/@name

XPath Style主要應用在XSL中。

一個JXPath項目能夠按照XPath的方式訪問Java Bean屬性。

http://jakarta.apache.org/commons/jxpath/

簡單的尋址,OGNL和XPath能夠對應起來。但是,OGNL和XPath都各自是功能很強大的語言,複雜的用法並不能對應。

評估指標

下面列出一系列比較詳細的、能夠落到實處的、能夠客觀量化的、可操作的評估硬指標。

排名不分先後。大家可以參考各自關心的選項。

雖然下面主要針對的都是Java Web 顯示技術,但這些指標同樣適用於其他語言的Web顯示技術。

評分採取10分作爲滿分。

(1) Host Language Consistency宿主語言一致性

Server Side Template Script和Server Host Language是同一種語言。這應該是專門針對JSP的優勢來說了。JSP能夠獲得10分。

另外,XSL也是。XSL本是也是XML格式。也能夠獲得10分。

其他的Template Script,如taglib,tapestry只能獲得0分。

freemarker, velocity由於具有一定的動態解釋的方便特性,可以獲得2分。

至於在Java Code裏面操作Template或者提供匹配數據的那些技術,由於Template中不存在Script Logic,能夠獲得5分。

大家可能不太注意這個特性。但是這個特性還是有一些意義的。其他的如ASP.net,還有動態語言,Ruby, Python, PHP, Perl等,都是Template Script和宿主語言一致。

這能夠一定程度上降低學習成本。尤其是宿主語言比較適合作爲Script的情況下。

(2)Template Purity 模板純淨度

這主要是指Template裏面沒有Script Logic代碼污染。

這方面,所有的Scripted Template技術都只能獲得0分。

XMLC能夠獲得10分,只利用HTML本身的Attribute,沒有增加任何自定義DOM Attribute。

Wicket, DOMPlus能夠獲得9分,它們增加了一部分自定義DOM Attribute。

JDynamiTe, Fastm能夠獲得7分,它們採用了XML Comment作爲自定義結構標籤。

Rife也能夠獲得3 — 7分,具體看它採用什麼標籤格式。

(3)Template Tidiness 模板整潔度

主要是指Template的格式是否整齊規範。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裏面直接大致正確顯示,設計人員友好。

XMLC, DOMPlus得分10。

Wicket得分9。

JDynamiTe, Fastm, (Rife根據情況) 得分8。

Tapestry得分7。HTML畢竟夾雜了Logic Tag。

JSP, Freemarker, Velocity, Taglib, XSL得分0。

Freemarker, Velocity屬於按行解析,有可能採取如下手段,把語句包含在XML Comment裏面,進行顯示友好的處理。這種情況下得分5。

#if ….

–>

由於Taglib的XML規範格式,使得某些IDE Plugin,DreamWeaver Plugin能夠顯示HTML Display Taglib。如果是對於此類Plugin來說,Taglib的所見即所得分數可以是0– 5分。類似於Tapestry,仍然是Logic Tag影響了最終得分。

(6)Action Code Purity 用戶代碼純淨度

主要是指用戶提供顯示數據的後臺Java代碼的純淨度,是否免除了HTML,或者Template操作的污染。

Servlet的HTML污染現象就非常嚴重。代碼裏面夾雜了大量的HTML Text。分數自然是0。

JSP, Freemarker, Velocity都能夠獲得10分。用戶後臺代碼十分純淨,不需要引入具體框架的代碼。任何一份Action Code,完全不用知道自己使用的是什麼Template,這三種Scripted Template都能夠隨意替換。能夠獲得10分。pojo

Taglib根據各種具體情況,能夠最高獲得8分。

Fastm, DOMPlus需要根據一定的約定,產生POJO數據。用戶Action Code同樣不需要引入具體的框架代碼,產生的這些數據同樣可以很容易地被其他Template,比如JSP, Freemarker, Velocity使用,能夠某種程度上替換Template。能夠獲得6分。

Tapestry需要在每份用戶Action Code裏面引入Template框架的Package。只能獲得4分。

Wicket不僅需要在每份用戶Action Code裏面引入框架的Package,還需要引入框架特殊的View Data Model數據類型,並且提供特殊類型的數據。只能獲得2分。

XMLC, Rife, JDynamiTe不僅需要在每份用戶Action Code裏面引入框架的Package,而且需要大量的Template操作。只能獲得0分。

(這項特性的比較,對於Tapestry,Wicket來說是不公平的。因爲它們的框架就包括了Template本身。Action裏面引入框架 Package是很正常的。而且這些框架同樣可以外接其餘的Template,只是原來的編程模型,需要做一些更改。這裏只是對於單項比較就事論事。)

(7) Infrastructure Code Purity 基架代碼純淨度

這裏是指框架的內部實現代碼裏面是否夾雜了HTML Text污染。這也意味着如果用戶需要擴展頁面UI組件,是否也需要在代碼裏面夾雜HTML Text。

HTML Taglib, Wicket, Tapestry的框架實現代碼裏面包含了很多HTML Text輸出語句。用戶需要自定義擴展頁面UI組件,也需要在代碼裏面夾雜HTML Text。所以,得分只能是0。

JSP, Freemarker, Velocity, XMLC, XSL, Rife, JDynamiTe, Fastm, DOMPlus得分都是10。

(8)動態Include

即運行的時候,動態選擇Include另外的Template文件。

JSP文件裏面的 @ include 屬於靜態Copy And Paste技術。

Jsp:include命令是動態Include.相當於
request.getRequestDispatcher(…).include(request, response);

Velocity, Freemarker的#Parse指令應該也是動態解釋執行的。也可以算是動態Include。

至於XMLC, Rife, JDynamiTe這類技術能夠隨意操作Template Node,動態Include也是小菜一碟。

Fastm, DOMPLus同樣提供了操作Template Node的能力,而且爲了避免這類Template Manipulation代碼污染,還提供了類似於XSL的Node Interceptor的機制實現動態Include。

XSL Apply Imports Call Template能夠動態引入並使用其他的XSL。

所以,JSP, Freemarker, XMLC, Rife, JDynamiTe, Fastm, DOMPlus, XSL的動態Include方面的分數都是10。

其餘的,Taglib, Wicket, Tapestry得分爲0。

(9)Recursive Display of Tree Data樹型數據的遞歸顯示

遞歸顯示一個任意深度的樹型數據,這是一個動態Include基礎上的更高級的需求。可以說,不支持動態Include,就不支持遞歸顯示。

遞歸,XSL無疑是天生贏家。XSL的Pattern Match語法可以說就是爲遞歸編寫的。

其餘的語法都是Imperative Programming。遞歸的前提是必須能夠定義一個方法,自己直接或者轉彎抹角的能夠調用到自己。

對於JSP, Velocity, Freemarker這類沒頭沒尾的Script來說,屬於強人所難。

Tapestry, Taglib, Wicket比較牛,專門提供了Tree Model。

XMLC, Rife, JDynamiTe這些Template Manipulator高興了,可以在Java代碼裏面任意根據數據任意操作Template Node。

Fastm, DOMPlus不僅可以在Java代碼裏面任意操作,而且提供了類似於XSL Pattern Match的Node Interceptor功能,不需要寫Template Node操作代碼,就可以實現遞歸。而且可以實現Data Iterator + Template Iterator的匹配序列。

遞歸方面,得分如下。

XSL, XMLC, Rife, JDynamiTe, Fastm, DOMPlus得分10。

Tapestry, Taglib, Wicket能夠顯示特定的Tree Model。得分4。

其餘的,得分0。只能通過Java代碼裏面夾雜一堆的HTML Text,然後整體輸出給Scripted Template來實現。

(10) Space Efficiency空間效率

基於Template Manipulation的技術都有空間效率問題。用戶同時訪問同一個Page的時候,內存中存在多個副本。XMLC的問題可能最重。因爲XML DOM結構很重。

JDynamicTe, Rife直接在一個Template Node上操作,如果有多個用戶同時訪問同一個Page。那麼同一份Template Node就會在內存中Duplicate多份。

空間效率方面得分情況

XMLC得分0。JDynamicTe, Rife得分3。如果靜態文本節點作了優化,分數可能更高。

Taglib由於編譯的結果非常臃腫,Tag之間的信息交流非常困難。分數爲6。

DOMPlus一份DOM產生多份SAX Event,沒有嚴重的多副本問題,但是DOM結構本身比較大,所以得分爲6。

其餘的技術,內存裏的靜態文本都只保存一份,都沒有嚴重的空間效率問題,得分都是10。

(11) Mapping Explicitness 映射關係明顯度

什麼數據應該顯示在什麼位置,一目瞭然。這種特性。

JSP, Velocity, Freemarker直接在Template裏面把數據取出來顯示,一目瞭然,清清楚楚,得分都是10。

Wicket的強制View Model 類型這裏幫了大忙,無時無刻不提醒用戶Model 和 View (Template)之間的映射關係。得分8。

XMLC直接操作HTML Node By ID, or By Generated Method, 得分爲7。

比起,JSP等來說,Taglib的映射關係就隔了一層。尤其是當Tag之間存在層次關係的時候,比如,Form Tag下面的Input Tag,Select Tag下面的Option Tag。Taglib的分數只有6。

XSL的XPath Pattern Match也是要稍微轉個彎,類似於AOP Interceptor的思路。得分爲5。

Tapestry的配置如此複雜,得分只有4。

Rife, JDynamicTe直接操作Template Node,而且是自定義層次的Template Node,用戶編寫Action Code的時候,必須隨時查看Template裏面的那些自定義標籤之間的層次關係,並完全理解,瞭然於胸,纔可能編寫正確的代碼。這方面的成本大大提 高。分數只有3。

Fastm, DOMPlus的問題類似,也是自定義層次的Template Node,需要隨時查看Template裏面的那些自定義標籤(或者DOM Attribute)之間的層次關係。分數只有3。

(12) Display Logic Reusability 顯示邏輯重用度

嵌在Template裏面的Server Side Script代碼,不具有任何可重用性。除了整個Include,你無法在另外的地方調用Template裏面的某一段代碼。

JSP, Velocity, Freemarker, Logic Taglib, Tapestry Logic Tag,XSL的邏輯可重用度分數都是0。當頁面設計人員更改了具體頁面佈局元素(HTML Tag)的時候,原來的Template裏面的Script全部作廢,需要重新填充到新的HTML裏面。

Template Manipulation和Model Match技術的顯示邏輯都存在後臺的Java代碼裏面,自然是可以重用的。方法調用,類繼承,包含,怎麼都行。

Wicket的View Model都是綁定到具體的HTML UI Tag上,比如,List, Table等。當這些Tag變化較大的時候,原有的代碼都需要改變。某些HTML Display Taglib也是如此。重用度分數爲4。

當結構層次沒有變化,只是具體的HTML Tag變化的時候,XMLC的原有DOM處理代碼幾乎不需要變動。在處理循環的時候,代碼需要Create Specific HTML DOM Node,然後添加到某個DOM Node上面。而且代碼可能大量使用自動產生的代碼的方法。這影響了它的得分,分數爲4。

當結構層次沒有變化,只是具體的HTML佈局元素髮生了變化,那麼,Rife, JDynamiTe,的代碼都不需要變化。但是,它們的代碼侵入性非常強,比XMLC還要強(如果XMLC採用標準的HTML DOM操作方法)。權衡考慮,Rife, JDynamiTe的重用度分數是5。

當結構層次沒有變化,只是具體的HTML佈局元素髮生了變化,Fastm, DOMPlus的代碼也不需要變化。而且,Fastm, DOMPlus沒有代碼侵入性,產生的Data Model就是POJO,可以用在JSP, Velocity, Freemarker,Taglib裏面。所以,重用度分數爲8。

Scripted Template

前面講述了評估指標。下面分別各項技術進行單項說明。

(1) Scripted Template

HTML和Server Side Script混雜在一起的顯示層技術。

包括JSP, Velocity, Freemarker, Taglib, Tapestry, XSL等。

Server Side的這些Scripted Template技術比較流行,耳聞能詳。前面進行指標描述的時候,各種參數,也基本上涉及到了。就不具體展開進行單項的用法說明和特性分析。

JSP, Velocity, Freemarker的優勢在於這些技術對用戶後臺Java代碼侵入性非常低,這些Template都可以任意替換,而不影響用戶後臺Java代碼。

下面講述另外兩類不是很常見的技術。

(2)Template Manipulation

Java代碼直接操作Template(比如,HTML DOM)產生結果的顯示層技術。

包括XMLC, JDynamiTe, Rife等。

(3) Model Match

Java代碼負責提供符合顯示層要求的Data Model,顯示層框架本身把Data Model和Template進行匹配,產生結果。

包括Wicket, Fastm, DOMPlus, 等。

Template Manipulation

Java代碼直接操作Template(比如,HTML DOM)產生結果的顯示層技術。

包括XMLC, JDynamiTe, Rife等。

這類技術都具有良好的所見即所得特性。

(1)XMLC

http://xmlc.enhydra.org/

XMLC把一個HTML文件翻譯成一個Java HTML DOM Class,

比如,

Hello, World

… …

這些具有id的HTML元素,在Java HTML DOM Class都產生了對應的方法。

HTMLElement getElementPara1()

public void setTextPara1(String text)

HTMLTitleElement getElementTitle()

HTMLInputElement getElementNameInput();

比如, CLASS=”class1 class2″>

就產生了如下的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的添加刪除操作。而且裏面使用的代碼都不是標準的DOM操作方法,而是代碼生成的方法。代碼侵入性非常強,如果要換成別的Template,比如JSP, velocity所有的代碼都要作廢。

當然XMLC產生的是一個DOM,後面還是可以接續XSL的。

一般來說,XML DOM操作只能針對完整的Node。一般需要替換整個Attribute,整個Text。

對於,

另外有一個不常見的需求。動態替換Java Script代碼的裏面的某一部分。這時候,XMLC就完全無能爲力了。或許也可以引入外來的Text Parser Engine,比如Velocity, Freemarker, Fastm, JDynamicTe等來做這件事情。

XMLC的主要問題還是空間效率問題。每次請求,用戶需要產生一個Java DOM Class副本,進行操作。如果有多個用戶訪問同一個Page,那麼就同時存在多個Java DOM Class副本。

當然裏面的靜態文本資源是共享的,我們看到上面的Java DOM Class裏面,產生了很多String常數。

但是DOM Node結構本身的尺寸就比較大。即使採用了一些優化簡化的DOM Parser,去除了用不到的結構,整個尺寸還是比較大。

(2) JDynamiTe

http://jdynamite.sourceforge.net/doc/jdynamite.html

JDynamiTe是PHPLib Template的移植。採用XML Comment的方式標記動態結構塊。

我們來看一個典型的兩層循環的例子。

{VALUE_X} {VALUE_Y}

對應的代碼是,

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) 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的代碼侵入性非常強,用了之後,如果要換別的Template,比如JSP, velocity,這段代碼完全作廢。

(3) Rife

http://rifers.org/

類似於JDynamiTe,Rife也採用自定義動態塊標籤。

下面是一個典型的例子。遞歸顯示一個Data Tree。下面只是核心片斷。如果對整個例子感興趣,可以去Rife的網站查看Sample和Tutorial。

這是一段Tree Template。

${v level/}

${b level}

  • ${v nodes/}

${/b}

${b node}

${v title/}${v level/}

${/b}

對應的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的代碼侵入性強的缺點。如果需要換別的Template技術,比如JSP, velocity,整個代碼都要做廢。

Model Match

Java代碼負責提供符合顯示層要求的Data Model,顯示層框架本身把Data Model和Template進行匹配,產生結果。

包括Wicket, Fastm, DOMPlus, 等。

這類技術都具有良好的所見即所得特性。

(1) Wicket

http://wicket.sourceforge.net/

Wicket類似於Tapstry,採用HTML自定義Attribute作爲自定義標籤。

這段是Rife的一個典型的循環的例子。wicket:id一個標籤,幾乎可以滿足任何需求。有興趣的讀者可以去Wicket網站查看完整的Sample。這裏只有核心片斷。畢竟,本文不是一部Wicket教程。

 
發佈了36 篇原創文章 · 獲贊 0 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章