2010年2月12日 星期五

徹底瞭解 GWT Part 2:JavaScript 的 overlay type

原文:http://googlewebtoolkit.blogspot.com/2008/08/getting-to-really-know-gwt-part-2.html

技術校正、審閱:tkcn

假設你已經在 GWT module 當中,愉快地使用 JSNI 來呼叫某些手寫的 JavaScript。一切運作正常,但是 JSNI 只能在獨立的 method 下運作。某些整合性狀況需要你徹底地把 JavaScript 跟 Java 的 object 綁在一起——寫 DOM 跟 JSON 就是兩個好例子——所以我們十分需要可以從 Java 程式碼直接與 JavaScript object 互動的方法。換句話說,我們想要 JavaScript 的 object 看起來就像我們寫的 Java object。

GWT 1.5 引入了 JavaScript overlay type,這讓 GWT 程式整合各種 JavaScript object 變得容易許多。這個技術有很多好處,像是讓你能用 Java IDE 的 code completion 跟 refactoring 功能,即使你寫的是 untype 的 JavaScript object。

範例:簡單、有效率的 JSON
用一個範例來瞭解 overlay type 是最簡單的方法。假設我們要存取一組「customer」數據,底層是用 JSON object。在 JavaScript 中的資料結構可能像這樣:
void jsonData = [
{ "FirstName" : "Ps", "LastName" : "Monkey" },
{ "FirstName" : "痞子", "LastName" : "猴" },
{ "FirstName" : "Pt2", "LastName" : "Club" },
{ "FirstName" : "STO", "LastName" : "Orz" },
];

要把一個 Java type 加到上述的資料結構,要從建立一個 JavaScriptObject 的 subclass 開始,這在 GWT 表示是一個 JavaScript 的 object。接著增加一些 getter。
// An overlay type
class Customer extends JavaScriptObject {
// Overlay types always have protected, zero-arg ctors
protected Customer() { }

// Typically, methods on overlay types are JSNI
public final native String getFirstName() /*-{ return this.FirstName; }-*/
public final native String getLastName() /*-{ return this.LastName; }-*/

// Note, though, that methods aren't required to be JSNI
public final String getFullName() {
return getFirstName() + " " + getLastName();
}
}

如此一來,GWT 就會瞭解所有 Customer 的 instance 實際上是來自 GWT module 以外的 JavaScript object。這包含了很多意義。舉例來說,看到 getFirstName()getLastName() 裡頭的 this reference。它實質上是代表一個 JavaScript object,所以你操作這個 this 就像在 JavaScript 裡頭一樣。在這個例子中,我們可以直接存取 JSON 中那些我們已知的 field:this.FirstNamethis.LastName

那麼,你要如何才能真正得到一個被包裝成 Java type 的 JavaScript object 呢?你不能用 new Customer() 來建構它,因為重點是把一個既有的 JavaScript object 包裝成 Java type。因此,我們必須使用 JSNI 來得到這樣一個 object:
class MyModuleEntryPoint implements EntryPoint {
public void onModuleLoad() {
Customer c = getFirstCustomer();
// Yay! Now I have a JS object that appears to be a Customer
Window.alert("Hello, " + c.getFirstName());
}

// Use JSNI to grab the JSON object we care about
// The JSON object gets its Java type implicitly
// based on the method's return type
private native Customer getFirstCustomer() /*-{
// Get a reference to the first customer in the JSON array from earlier
return $wnd.jsonData[0];
}-*/;
}

現在來搞清楚我們做了啥。我們拿到了一個 plain old JSON object(譯註:源自於 POJO)並且建立一個看起來很正常的 Java type,讓 GWT 程式碼中能夠使用它。於是你就有了 code completion、refactoring、compile 階段的檢查——這些寫 Java 時所擁有的好處。然而,你還是可以靈活地操作任何 JavaScript object,這使得存取 JSON service(使用 RequestBuilder)變得很輕而易舉。

為一些 compiler 強者岔題一下。overlay type 另一個美妙的事情是你可以增加 Java 的 type,但是卻不用影響底層的 JavaScript object。注意到上面例子中,我們加入的 getFullName() 這個 method。它是純粹的 Java 程式碼(並不存在於底層的 JavaScript object),但卻是依照底層 JavaScript object 所寫的。也就是說,處理同一個 JavaScript object,以 Java 的角度會比用 JavaScript 功能豐富得多;而且不用動到底層的 JavaScript object——無論是 instance 或是 prototype

(接續上一段的題外話)在 overlay type 增加 method 這個很酷的怪招是可行的,因為 overlay type 的設計規則是不允許 polymorphic 呼叫,所有的 method 必須是 final 且/或 private。因此,compiler 是靜態地解讀每一個 overlay type 的 method,所以不需要在 runtime 的時候動態 dispatch。這是為甚麼我們不用拘泥在 object 的 function pointer;compiler 可以直接對 method 呼叫,就好像是 global function、獨立於 object 之外。很明顯的,直接呼叫 function 會比間接快得多。更棒的是,因為呼叫 overlay type 的 method 是靜態解讀的,這些動作會嘗試自動 inline;這在為了 script 語言的效率而奮戰時,是非常強大的火力支援。接下來我們會重新來一遍,展示給你看這個方法有多成功。

範例:lightweight collection

我們在上面的例子當中掩蓋了某些事情。getFirstCustomer() 這個 method 是非常不切實際的。你一定會希望存取全部的 customer 陣列。所以,我們需要一個 overlay type 來表示這個 JavaScript 陣列。幸運的是,這很簡單:
//泛型在 overlay type 裡頭也運作正常!
class JsArray<E extends JavaScriptObject> extends JavaScriptObject {
protected JsArray() { }
public final native int length() /*-{ return this.length; }-*/;
public final native E get(int i) /*-{ return this[i]; }-*/;
}

現在我們可以寫出更有趣的程式了:
class MyModuleEntryPoint implements EntryPoint {
public void onModuleLoad() {
JsArray<Customer> cs = getCustomers();
for (int i = 0, n = cs.length(); i < n; ++i) {
Window.alert("Hello, " + cs.get(i).getFullName());
}
}

// Return the whole JSON array, as is 
private final native JsArray<Customer> getCustomers() /*-{
return $wnd.jsonData;
}-*/;
}

這是一個很乾淨的程式碼,尤其是以建立靈活配置的角度來看。正如上頭提到的,compiler 可以作一些十分 fancy 的事情,讓它相當有效率。看一下 onModuleLoad() 這個 method 在沒有 obfuscate 的 compile 結果:
function $onModuleLoad(){
var cs, i, n;
cs = $wnd.jsonData;
for (i = 0, n = cs.length; i < n; ++i) {
$wnd.alert('Hello, ' + (cs[i].FirstName + ' ' + cs[i].LastName));
}
}

這個最佳化真的是 xx 的好。即使是 getFullName() 這個 method 的 overhead 也沒了。事實上, 所有 Java method 的呼叫動作都不見了。當我們說:「GWT 給你可負擔的 abstraction」,這就是其中之一。不僅 inline 的程式碼執行速度明顯變快、我們不再需要定義 function 的內容、也因而得以將 script 簡短化(雖然持平而論,inline 的方式也很容易讓 script 量變多,所以我們小心地在速度與程式碼大小之間取得平衡)。現在回顧上頭原始的 Java 程式碼是十分有趣的,而試著推導 compiler 最佳化的步驟就展示到這邊。 不過,我們還是忍不住要 show 一下對應、obfuscate 過的程式碼:
function B(){var a,b,c;a=$wnd.jsonData;for(b=0,c=a.length;b<c;++b){  $wnd.alert(l+(a[b].FirstName+m+a[b].LastName))}}

注意在這個版本當中,唯一沒有 obfuscate 的是 JavaScript 當中的識別字,例如 FirstNameLastNamejsonData 等。這是為甚麼即使 GWT 努力讓大量 JavaScript 交互溝通的操作變得容易,但我們還是努力說服別人盡量用純 Java 來寫程式、而不是混著 JavaScript 寫。希望你聽到我們講這些之後,你會明白我們不是要打擊 JavaScript——只是我們不能對它們最佳化,這會讓我們很沮喪。

摻在一起作撒尿牛丸 
overlay type 是 GWT 1.5 的重要特性。這個技術讓直接與 JavaScript library 互相溝通變得相當容易。希望在讀完這篇文章之後,你可以想像如何以一組 Java type 直接導入任何 JavaScript library 到 GWT 裡頭,進而使用 Java IDE 來進行高效率的開發跟 debug,卻不會因為任何類型的 GWT overhead 而影響程式碼大小或執行速度。同時,overlay type 作為一個強大的 abstraction 工具,提供更優雅的低階 API,例如新的 GWT DOM package

2010年2月5日 星期五

徹底瞭解 GWT Part 1:JSNI

原文:http://googlewebtoolkit.blogspot.com/2008/07/getting-to-really-know-gwt-part-1-jsni.html

技術校正、審閱:tkcn

[前面略過一段很口語、很難翻譯、但是不太重要的一段 XD]

徹底瞭解 GWT,第一步:JSNI
如果你是 GWT 新手,你可能覺得奇怪:到底有什麼好激動的?GWT 跟其他 framework 有什麼不同?GWT 是一個基礎技術、串起許多工具,而不只是特定 application 的 framework。因此,雖然 GWT「有」很多 library,覺得有用的話,要用多用少都可以。不喜歡 GWT 的 UI?你可以用 DOM 建立自己的 UI。不想用 RPC 而想要用 JSON?那也沒問題。事實上,要用 GWT 重頭開始打造自己的  framework 是絕對沒問題的,也能保留所有 GWT debug 跟 compile 方面的優點。

所以,用一個宏觀的角度來思考:為什麼你要考慮使用 GWT 來開發下一個大的 web application 呢?
  • 相較於傳統 server-centric 的方式,如果 AJAX 設計得好,因為本身的特性,在許多種類的 application 上會提供更好的使用經驗。你都已經在讀這個 blog 了,可能早就知道啦。
  • GWT compiler 產生的 JavaScript 碼通常會比你自己撰寫的程式碼還要小、還要快。
  • 用 GWT 開發可以讓你更有產能、而且產生的 JavaScript 也更加可靠。
  • You get the opportunity to say "Gwit" a lot.

我們擔心你是那種容易被一些主張或是行銷說詞給影響了,所以我們在接下來幾篇 blog 文章中,會看到 GWT 如何實現性能的提昇、以及如何讓 JavaScript 使用起來更容易。

在深入技術細節之前,還有一件事。我們有時會問兩個跟 GWT 本質有關的問題:
  1. 為什麼 GWT 以 Java 語言和工具為核心?
  2. 跟手寫的 JavaScript 比起來,GWT 是不是比較多限制、也比較 heavyweight?

先回答第一個問題,請瞭解我們的目標跟滿腔熱血,是為了徹底改善網頁使用者的使用者經驗,這表示 GWT 產生的 JavaScript 必須有最好的效能跟可靠度。為了做到這一點,我們自然想加入一堆程式碼的最佳化,並且盡可能的提早抓出 bug。這兩個目標都容易在 Java 的 static type、以及既有的 Java IDE 來達成。這是為什麼我們冷靜地選擇了 Java 技術作為 GWT 的核心。就是這樣—沒啥語言聖戰的梗。

再來回答第二個問題,有些開發人員在第一次聽到 GWT 時,會假設它是一個 abstraction 的「Walled Garden」,讓你永遠只能用 Java 寫程式而不讓你使用或是整合手寫的 JavaScript。這實在錯的很離譜......

GWT 中的 JavaScript Native Interface(JSNI)
你可以輕鬆地把手寫的 JavaScript 直接嵌入 GWT 程式碼當中。反正最後都會變成 JavaScript,那為甚麼不提供 GWT 的開發人員一個有用的方法,讓他們能夠混合這兩個東西?這方法就叫做 JSNI 啦。它的名字跟 JNI(Java Native Interface)很像,因為他們的基本想法也一樣:把一個 Java method 宣告成「native」然後用另一個語言來實做內容。在 JSNI 當中,就是用 JavaScript 來實做內容。

用 JavaScript 寫 Java Method
JSNI 在創造功能最底層的 abstraction 是非常有用的,它很自然地使用 JavaScript、而不是 Java 語法。例如 Regular Expression 在 JavaScript 是相當簡潔的,所以你可以透過 JSNI 在 GWT 程式碼當中直接使用它們。假設你想把某人的名字(Ps Monkey)轉過來,讓姓在後面、名字在前面(Monkey, Ps)(當然,這在 I18N 當中會是一場惡夢)。你可以建立一個很簡短的 JSNI method 來做到:
// Java method declaration...
native String flipName(String name) /*-{
// ...implemented with JavaScript
var re = /(\w+)\s(\w+)/;
return name.replace(re, '$2, $1');
}-*/;
請注意,method 的內容整個被特殊標記「/*-」、「-*/」給包圍起來,變成一段 Java 的註解。

在 JSNI 中呼叫 Java method
你也可以反過來,在 JavaScript 呼叫一個 Java 的 method。假設我們修改上面的例子,改成這樣:
package org.example.foo;
public class Flipper {
public native void flipName(String name) /*-{
var re = /(\w+)\s(\w+)/;
var s = name.replace(re, '$2, $1');
this.@org.example.foo.Flipper::onFlip(Ljava/lang/String;)(s);
}-*/;

private void onFlip(String flippedName) {
// do something useful with the flipped name
}
}

用 JSNI 存取外部的 JavaScript 程式碼
當然,你可以在 GWT module 存取任何外部的 JavaScript 程式碼。舉例來說,如果你的 HTML 頁面看起來像這樣:
<html>
<head>
<SCRIPT>
function sayHello(name) {
alert("Hello from JavaScript, " + name);
}
</script>
<-- Include the GWT module called "Spiffy" -->
<script src="org.example.yourcode.Spiffy.nocache.js"></script>
</head>
...
在 Java 程式碼,您可以用 JSNI 去呼叫 JavaScript 的 sayHello() function:
// A Java method using JSNI
native void sayHelloInJava(String name) /*-{
$wnd.sayHello(name); // $wnd is a JSNI synonym for 'window'
}-*/;
GWT compiler 把外部的 method 呼叫給內嵌進來,所以在 Java 當中呼叫 sayHelloInJava() 的效率並不會比直接在 JavaScript 當中呼叫來的差。

用 GWT 建立 JavaScript library
你甚至可以用 GWT 創造一個 JavaScript 可以呼叫的 library。這是一個非常棒的技巧:
package org.example.yourcode.format.client;
public class DateFormatterLib implements EntryPoint {

// Expose the following method into JavaScript.
private static String formatAsCurrency(double x) {
return NumberFormat.getCurrencyFormat().format(x);
}
// Set up the JS-callable signature as a global JS function.
private native void publish() /*-{
$ wnd.formatAsCurrency =
@org.example.yourcode.format.client.DateFormatterLib::formatAsCurrency(D);
}-*/;

// Auto-publish the method into JS when the GWT module loads.
public void onModuleLoad() {
publish();
}
}

接著,你可以在任何 HTML 或是 JavaScript library 當中存取這個 GWT 做出來的功能:
<html>
<head>

<-- Include the GWT module that publishes the JS API -->
<script src="org.example.yourcode.FormatLib.nocache.js"></script>

<-- Write some JS that uses that GWT code -->
<SCRIPT>
function doStuff() {
alert(formatAsCurrency(1530281));
}
</script>
</head>
...

Ray Cromwell(最有名的是 GWT Extreme!)在他的 GWT Exporter 淋漓盡致地採用了上述的 JavaScript 發佈技術。它用 GWT compile 期的 code 產生器來自動創造所有發佈出的程式碼。(「compile 階段的程式碼產生器」聽起來很酷吧?如果你也這樣覺得,那請期待這個系列的後續文章。)

摻在一起作撒尿牛丸
用 JSNI,你可以用你想要的方式,自由地混和手工撰寫的 JavaScript、外部的 JavaScript library、以及 Java 程式碼。這就是我們說 GWT 並不是一個 abstraction 的「walled garden」,因為你可以逐步地將 GWT 融入到既有的 web application 當中。更棒的是,你可以用你喜歡的 Java debugger 來 debug 所有 Java 程式碼。

想要了解更多的話,請看 GWT Developer Guide 的 JavaScript Native Interface。或著,讓 GWT compiler 的設計師 Scott Blum 解釋給你聽也不錯喔。

2009年12月22日 星期二

用 GWT 寫個「生命遊戲」吧!

原文寫於 Java 版(ptt.cc)

以 GWT 教學系列來說,應該先講環境建制之類 blahblah
不過還是跳過那些繁文縟節,先講比較實在的部份
如果這樣子寫 JavaScript 的方式沒辦法引起興趣
還是會想要用 jQuery 之類的方法來寫程式
那開發環境安裝起來再快(例如 XAMPP [大笑]),也沒啥意義 :P
------------

寫一個 JavaScript 版的生命遊戲(Game of life)
要花多久? 有多難呢?

嗯? Java 版不是說好不提 JavaScript 嗎?
好好好,那拿出以前交作業的 Java 程式吧! [咦?]

首先包個「細胞」class
狀態只有兩種,要嘛活著要嘛死掉
不過得多個變數預知下一代會生會死
public class Cell{
private boolean now;
private boolean next;
public Cell(){}

public Cell(boolean alive) {now = alive;}

public boolean isAlive(){return now;}

public void setNextAlive(boolean alive){
this.next = alive;
}

public void nextTurn() {now = next;}
}
然後再來包個「世界」的 class
裡頭有一堆細胞,所以用個二維陣列來表示
至於 width, 跟 height 這兩個 field 純粹只是為了懶惰好看 [逃]
public class World{
private int width;
private int height;
private Cell[][] cell;

public World(int w, int h){
width = w;
height = h;

cell = new Cell[w][h];
for(int i=0; i<2; i++){
tmpW = w+i;
if( tmpW<0 || tmpW==width){
continue;
}
for(int j=-1; j<2; j++){
tmpH = h+j;
if( tmpH<0 || tmpH==height){
continue;
}
if(tmpH==h && tmpW==w){
continue;
}
if(getCell(tmpW, tmpH).isAlive()){
sum++;
}
}
}

switch(sum){
case 0: case 1: case 4: case 5:
case 6: case 7: case 8:
getCell(w, h).setNextAlive(false);
break;
case 3:
getCell(w, h).setNextAlive(true);
break;
}
}

public Cell getCell(int w, int h){
return cell[w][h];
}
}
好了,物件都包好了,那麼現在該來解決畫面了
這次不用 Swing、也不用 SWT,試試傳說中的 GWT [奸笑]

「監視器」本身是個 panel,至於 vertical 在幹麼就跳過。
監視器要監看生命遊戲的世界,還要有一個棋盤來顯示狀況
所以 field 大概就是這樣啦~
public class Monitor extends VerticalPanel{
private World world;
private Grid board;
private int width = 10, height = 10;
}
constructor 要把「世界」跟「棋盤」正式建立起來
「棋盤」要放在「監視器」上頭,還要有個按鈕讓人家可以點下一步
所以...
public Monitor(){
world = new World(width, height);

board = new Grid(width, height);
this.add(board);

Button next = new Button("下一世代");
//點下一步要幹些什麼好事?
next.addClickHandler(new ClickHandler(){
@Override
public void onClick(ClickEvent event) {
nextTurn();
}
});
this.add(next);

//重新整理畫面
refresh();
}
這邊先跑出來兩個新的 method
nextTurn() 沒啥好講的,就是要 world 過個年,然後重新整理畫面
public void nextTurn(){
world.nextTurn();
refresh();
}
至於 refresh(),我打算讓活著的細胞用「X」來表示
死掉的細胞的空空白白的什麼都沒有(其實是全形空白)
private void refresh() {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (world.getCell(i, j).isAlive()) {
board.setText(i, j, "X");
} else {
board.setText(i, j, " ");
}
}
}
}
剩下的就是找到啟動的地方
在 GWT 裡頭是 EntryPoint 這個家族的 class 然後這樣寫:
public class Enter implements EntryPoint {
public void onModuleLoad() {
RootPanel.get().add(new Monitor());
}
}
好啦~ 恭喜你,只剩下 compile 以及找個網頁空間放 .js 檔
JavaScript 版的生命遊戲就... 寫 完 啦... \囧/

輕不輕鬆? 開不開心? [扭扭]
以前的 Java 程式碼可以直接拿來用...


阿? 什麼?
GWT 的開發環境要怎麼安裝?
程式碼要放在哪個目錄下?
怎麼指定 EntryPoint?
ㄜ... 請參照課本第 43 頁就可以了,好! 下課! [逃]

2009年12月11日 星期五

GWT 2.0 介紹

原文網址:http://googlewebtoolkit.blogspot.com/2009/12/introducing-google-web-toolkit-20-now.html

今晚稍早,令人興奮的 Campfire One 結束了,在這個聚會上,我們宣佈 GWT 2.0 正式發佈。除了 GWT SDK 與 Google Eclipse Plugin 的重大改進外,GWT 2.0 包含了給 Chrome 使用的全新效率分析工具,名為 Speed Tracer。

Speed Tracer 介紹
我們已經在前幾個禮拜中提到過,現在真的可以用啦。Speed Tracer 是在 Chrome 上頭的強力效能分析工具,前所未有地讓你切入到「任何」的 web application(不再只是用 GWT 寫的)。想知道為甚麼你的 web application 感覺這麼遲鈍嗎?Speed Tracer 可以幫你找到答案。

看 Speed Tracer 運作是十分有趣的,下面是一個快速介紹的影片:


更快的開發速度、更快的 application
這裡是另外一個快速導覽的影片,展示 GWT 2.0 當中一些很酷的功能:


現在,讓我們更進一步看看 GWT SDK 跟 Google Eclipse Plugin 多了哪些新東西:

新的 development mode 跟 draft compile 加快「編輯」與「重新整理」的速度

  • GWT 2.0 引進了「development mode」這個 debug 的新方法,大幅改進了「編輯」→「重新整理」的週期。這可以讓你在自己選擇的 browser 上頭 debug,不再是用 GWT 指定的 browser。development mode 需要名為「GWT Developer Plugin」的 browser plugin,掛上這個 plugin 的 browser 就會跟你的 Java project 建立起 debug bridge。先撇開技術細節,這種 debug 的方法真的感覺不賴。
  • development mode 把 debug 過程直接轉移到 browser 上,這讓你可以使用每個 browser 特有的 development tool、必且能和 GWT 的工具結合在一起。是的,你現在可以在 Firefox 上頭抓 Java 程式碼的錯誤,同時使用像 Firebug 這種超強工具來看 DOM 結構、測試 CSS。
  • 新的 Google Eclipse Plugin 優化了啟動、控制 development mode 的支援,包含了一個新的(Eclipse 中的)view ,可以直接看到 development mode 的 log 訊息。不在 Eclipse 裡頭啟動,而是利用 Swing 寫成的使用者介面(包含其他 IDE)來手工啟動 development mode 也是可以的。
  • development mode 也支援同時在多個 browser 作 debug。這表示你可以在同一個 debug 階段連接一個以上的 browser。這個接口相當有用,可以讓 development mode 的 session 持續運作一長段時間,卻讓你只需要簡單地 refresh browser 就可以看到 Java 程式碼改變的結果。這可以讓你快速地確認 project 可以在多數主要的 browser 正常運作,而不用重新啟動 Java debugger。
  • 還有還有,development mode 可以跨網路運作啦。這有什麼厲害的咧?因為你可以輕鬆地在你 coding 以外的平台上對 browser 作測試。舉例來說,假設你在 Linux 上頭的 IDE 寫程式,你可以在同一個 debug session 透過網路用 Windows 上的 IE 或 Chrome、OS X 上的 Safari 或 Firefox 執行你的 application,還可以 debug。重點是你可以在任何你喜歡的平台上頭開發,然後在每一個使用者可能使用的 browser/OS 組合上頭測試。
  • 雖然 development mode 會大幅度地減少 compile 的必要,但如果你還是常常需要 compile 成 JavaScript,你可以使用 GWT compiler 的新 flag「draftCompile」,這會省略最佳化的過程而加快 compile 的速度。你必須清楚瞭解,用這個方法產生的 JavaScript 不應該 deploy 出去,但是它在非成品輸出的不斷 build 階段,是節省時間的好東西。
用 UiBinder 來描繪 UI
  • GWT 2.0 引進一個建立 UI 新方法「UiBinder」,可以大幅提昇產能。UiBinder 的機制讓 UI 的外觀可以輕易地跟 application 的邏輯分離。在一個 UiBinder 的 XML template(*.ui.xml) 檔案裡頭宣告 HTML 跟 widget 就可以建構你的 UI 畫面。至於 application 的邏輯就放在相關的 .java 檔裡頭。
  • UiBinder 讓網頁設計師更容易直接切入開發流程中。舉例來說,開發人員可以複製貼上網頁設計師提供的 HTML mock。我們也看到,網頁設計師會喜歡直接修改 UiBinder XML template 然後在 development mode 當中快速的測試 UI 設計結果。用可互動的 widget 來設計,一定會比被動地用 HTML mock 來的有趣的多。
  • 這並不是說,你在用 UiBinder 時只能用內建的 widget。結合你自己的 widget 到 UiBinder template 跟使用任何 built-in widget 一樣。(譯註:感謝 tkcn 協助指正)
  • UiBinder 也可以協助預防微小的 bug,像是 ID 打錯,因為在 compile 的時候會交叉比對驗證 .ui.xml.java
  • Google Eclipse Plugin 現在提供 UiBinder 方面的建置精靈、code completion、red squiggly(譯註:spell check?)以及 refactoring,讓你用 UiBinder 更有效率。
Layout Panel 讓你有完美的視覺效果
  • 精準地弄出你想要的 look and fell 的 UI,向來是 HTML 跟 CSS 的一個棘手問題。在 GWT 2.0 之前,連 GWT 的 widget 也無法完整 abstract 出來,這導致某些 layout 很難做出來。不過,GWT 2.0 引入了「Layout Panel」機制,讓你可以精確地做出你想要的 layout。GWT SDK 裡頭那個 Mail 的範例已經改成這個方法,你可以從這裡看出它怎麼運作的。
  • Layout Panel 在標準的 CSS 上建立一個可預料、constraint-base 的 layout system。因為它跟 CSS 並存而不是不理會 CSS,Layout Panel 持續如預期顯示你需要載入的自訂 CSS。又因為 CSS-base 的 layout 實質上是由 browser 的 rendering engine 處理的,因此並不需要任何 JavaScript。所以,layout 非常快速而且流暢,你可以在調整 browser 視窗大小的時候感受特別深刻。
  • 正如你所預期的,Layout Panel 用 UiBinder 來操作特別順。只要幾行 XML,你就可以弄出非常精緻複雜的 layout,包括 animated transition、splitter...... 等等。
改進 Compiler
  • GWT 2.0 一個很關鍵的新功能是「developer-guided code splitting」。舉個簡單的比喻:你該不會希望在 YouTube 看影片的時候,是整部片下載完才能看吧?你會希望影片馬上開始,其他的部份繼續下載—web application 也是同樣道理。啟動 web application 不應該讓你感覺像是在「安裝」東西,而是當你按下 hyperlink 的時候就馬上開始。Code splitting 是非常有力的工具讓你實現這個想法。
  • 這聽起來很複雜,但是實際上 code splitting 相當簡單。只要找到你的 project 當中想要切出一些程式碼的點,然後用 GWT.runAsync() 這個新的神奇 method 來建立一個 split point。藉由胡亂增加 split point,你可以輕鬆且安全地切割你的 application,以確保一開始下載的部份是啟動程式的最低所需。Compiler 會安排其他的程式碼片段在稍後才下載。不像手動分割 JavaScript 檔,GWT compiler 把繁雜的工作都包好啦,會確保所有相依的部份會自動按照正確順序下載。GWT SDK 當中的 Showcase 範例已經更新成使用 code splitting,你可以去看看成效。
  • 除了 code splitting,compiler 還做了一些重要的改進,來增強產出 JavaScript 的能力。每個 GWT 版本釋出時,我們會改善 compiler 的最佳化技術,讓 JavaScript 更小也更快。當你已經有一堆 GWT project,這就很棒啦,因為你可以很簡單的升級、重新 compile 然後—嘿嘿,你的 application 就會啟動的更快、跑的更順。GWT 2.0 包含了一些強力效果的最佳化作法,我們也看到產出的 JavaScript 縮小了 3~20%。
用 ClientBundle 來最佳化 resource
  • GWT 1.4 有 ImageBundle 這個 resource-bunding 功能,ClientBundle 繼承這個 pattern 並且延伸出去。只要宣告一個簡單的 interface,就可以在 compile 階段把 resource 直接嵌入到最終的 JavaScript 還附帶最佳化。舉例來說,bunding 圖片可以讓你省去一堆 HTTP 溝通過程。ClientBundle 支援更多樣的 resource 種類,包含圖片、文字、甚至 CSS。
  • CSS?沒~錯!CssResource 是一個新的 framework 讓你可以有效管理 style。It supports a new extended CSS syntax for defining sprites, named constants, scopes, conditional selectors, and more. t also aggressively optimizes your CSS by stripping comments and whitespace and by obfuscating class names.(譯註:CSS 不熟,實在不知道怎麼翻 Orz)
  • ClientBundle 是可以擴充的。你可以創造自己的 resource 種類,然後 hook 進 ClientBundle framework,使得你找得到你要用的 resource 卻又能保持 interface 一致與簡潔。
(痞子:最後兩段「Special Thanks」跟「Getting Start」就懶得翻了 [茶])

2009年12月3日 星期四

GWT:這是什麼?幹什麼的?

就像看電影會先看預告片、買 A 片會先看封面
學 GWT 之前,先知道 GWT 是什麼? 能做什麼?
再來決定要不要學,這十分重要

生命中有這麼多美好的事情(例如 H-Game [誤])
不知道為了什麼而學(無趣的)新技術
實在是浪費時間虛擲生命

所以,GWT 是什麼呢?

他是 Java 界的救星
他是 JavaScript 界的剋星
更是網頁開發界的啤兒綠茶... \囧/


好好好,我言歸正傳就是了... [被毆飛]

〔GWT 是什麼?〕
是 Java、也是 JavaScript [扭]

GWT 全名是 Google Web Toolkit
望文生義,是用在網頁上頭的工具組

以軟體開發的角度,GWT 的核心是一個 Java compiler
處理 Java 程式碼,但輸出的不是 byte code
而是在 browser 上頭執行的 JavaScript

因此,為了方便開發時的測試與除錯
GWT 有 hosted mode,包括一個 JSP container 跟 browser
在 hosted mode 下,可以在 runtime 獲得一些錯誤訊息與資訊
這些訊息是由 GWT 提供,會比較好讀、貼近程式碼
(如果你想倚賴 browser 或 FireBug 來 debug,忘了他們吧! XD)
也能點出一些 GWT 才會有的錯誤
例如在 client 端的程式用了 java.util.Hashtable


以軟體架構的角度,GWT 的程式碼
分為 client 端以及可有可無的 server 端

client 端的基礎是 JSNI(JavaScript Native Interface)
Java 無法涵蓋 JavaScript 的部份
尤其是 browser 層級的操作
可以用 JSNI 的寫法包成 Java 可呼叫的 method

GWT 提供基本開發需要的 class 與相關機制
例如 UI componet、event handling、Http wrapper、JSON parser...
已經把 JSNI 包的差不多了
也許你寫完一個 project 都還看不到 JSNI 的影子

server 端的部份,主要是 GWT RPC
RPC 原文是 Remote Procedure Call
簡單地說,這讓你可以幾乎毫無感覺地使用遠端的 method/object
這裡的「遠端」當然是指 web server

GWT RPC 把 HTTP 的傳輸過程包起來
要傳遞的資料,封裝跟 parser 的手續也包起來
不管是在寫 client 端還是 server 端
都不用煩惱這些繁文縟節(而且,都是用 Java 寫!)
Java Compiler 還幫你作 type checking

真是太棒了阿... 這麼好的東西我以後用不到怎麼辦 <囧>

等等... 前頭不是說:server 端是可有可無的嗎?

是的,用 GWT RPC 有一個前提,server 必須是 JSP container
如果你是用 PHP 或是其他語言
又或著你想使用 JSON、XML 的方式來傳遞資料
那麼,GWT RPC 是可以完全不去理會的

〔GWT 能做什麼?〕
client 端? server 端? 傻傻的不用分清楚!

純粹以 JavaScript 的角度來說
JavaScript 能做到的「效果」(不是語言特性 XD)
都能以 Java code 寫出來
在某些情況,(大多是沒有 library 幫你包好 囧>)
才可能需要寫 JSNI 來做到底層控制

以網頁開發的觀點
GWT 能讓你以傳統 application 的思維來面對網頁程式

以 browser 的角度來看,GWT 用 JavaScript 操作 DOM
來產生 & 控制所有的畫面
(再囉唆一次,寫的時候還是用 Java XD)
因此當畫面改變時,頁面不用切換,變數也就不用傳遞
所以,你不用思考 JavaScript 變數要怎麼傳到下一個畫面
也就不用煩惱到底要塞 session 還是跟著 URL 帶著走

如果你用 GWT RPC,那更徹底
除了 callback 的寫法比較怪了點
其餘的部份,你很難分辨是在寫 Java application 還是在寫網頁
object 內容的維持、與 server 的同步也不再是瑣事 or 難事

對於會寫 Java Application 的人而言
要跨進 web 開發的領域
不用把思路切換成一頁一頁的方式、
不用學一堆新技術新語言、
不太需要知道哪些是 server/client 處理
甚至感覺不到 stateless
以開發的角度,這不是很美妙的一件事情嗎?

註:這裡不討論安全性的議題
(迷之聲:因為有人不會 [指])


〔GWT 不能做什麼?〕
不能讓你寫 JavaScript... [誤]

JavaScript 作不到的事情,GWT 也作不到
雖然這根本就像在說廢話 XD
但是當你在撰寫 client 端的程式時
得時時刻刻想起這句話
不然,寫到忘我的時候
很順手的就想讀個 File、連個 DB
Eclipse 這類的 IDE 也不會出現 compile 錯誤
(在 GWT compile 時還是會炸出錯誤訊息)


我研究 GWT 還不是很透徹、其他 JavaScript 的技術也沒在碰
所以 GWT 這個架構不能做什麼? 我實在回答不出來 [毆飛]

這邊只能提出一個「可能性」
就是 GWT 要整合其他 JavaScript 的技術
也許很困難 or 不可能?
之前要嘗試用 jQuery 將 GWT 生出來的 TextArea
裝上彩色模擬 scroll bar 就屢戰屢敗 Orz
(這跟當時懵然無知也很有關係 [炸])
換個角度想,用 GWT 是一定可以做出同樣效果
只是目前可能還沒有人寫 or release 出來

跟 jQuery 比起來,base on GWT 的 UI component(包含 3rd party)
的確是很量少虛弱又天生難看(靠自己的化妝技術彌補)
但這只能算是暫時的缺點,不能算是「作不到的事情」


〔為甚麼要用 GWT〕
因為這是一種 Rock'n Roll 的 style!

如果你喜歡一個 project 同時有好多技術
如果你喜歡自己掌握 HTTP/HTML/JavaScript 的所有細節

或著,你討厭 Swing 那類的 UI code
又甚至,你壓根討厭 Java 的一切

那麼,千萬、千萬不要碰 GWT

反過來說,如果你跟我差不多笨(好可憐阿 [炸])
對 Java 死心塌地,寫起來如吃飯睡覺一般自然
那麼,GWT 會是一個契機、一個救贖、一個捷徑

好了,接下來就要開始進入正題了......