2012年12月23日 星期日

在 2013 年會改變 Web 的五個 API

原文網址:http://blog.alexmaccaw.com/the-next-web

本篇沒有很講究翻譯上的精確度,可能會有漏字 or 自行腦補。另外原文有對一些字設定 style,並沒有完整一一對應到譯文中。

看 Web 的發展實在讓人興奮爆了,即將到來的 2013 年會有更多的進步。在接下來的一年中,有許多可能會從本質上改變我們開發與使用 Web 方式的技術會陸續出現。

2012年12月9日 星期日

2012 年 GWT 調查報告中文摘要

原始報告請到 https://vaadin.com/gwt/report-2012 下載。雖然 Vaadin 有收集 EMail 之嫌 [炸],但不可否認這份報告實在做的非常有愛,值得給他 EMail 帳號以資鼓勵。另,本篇摘要翻的很隨性,只保證數據沒有打錯,其他就...... [毆飛]


GWT 在 2012 年有許多令人興奮的內容、也改變了未來發展 GWT 的方式。 在產品與社群前所未有地茁壯的同時,大家想問:「阿下一步咧?」

Google 在六月的時候把 GWT 變得更開放。 為了未來的發展,成立了 GWT 指導委員會, 裡頭有 Vaadin、Google、Sencha、RedHat 等等。 我們開始設法收集大家拿 GWT 作啥、想許什麼願望。

這個 GWT 研究是由 Vaadin 以及一堆人努力而成, 在這份報告裡頭也會看到他們的意見。

在回收的一千三百多份問卷當中,都回答了超過三十個以上的問題, 我們相信這會是最完整的調查報告。

2012年12月5日 星期三

謠言終結者:一定要在 Tomcat 前端用 httpd 來增加效能嗎?

原文網址:http://www.tomcatexpert.com/blog/2010/03/24/myth-or-truth-one-should-always-use-apache-httpd-front-apache-tomcat-improve-perform

簡單地說,這是假的。要仔細講的話,在 Tomcat 3 的年代是有一些真實成份在。不過以現今使用的 5.5.x 以及 6.0.x 來說,實在沒有必要因為單純效能的理由來使用 httpd。Tomcat 現在支援 native / APR 的 connector,使用與 httpd 相同的 native library(Apache Protable Runtime APR)來處理低階 I/O,因此可以達到與 httpd 差不多的效能。當處理靜態內容時,Tomcat 是比 httpd 多一小點兒的負荷量,但實在太小了以至於在 production system 上很難注意到差異。

如果你研究 httpd 與 Tomcat 的效能議題,你會找到各種覆載與效能的 benchmark,結果也不盡相同。一個常被引用的結果證明 Tomcat 的純 Java connector 始終比 httpd 快

這個異常的結果剛好與期待相反:httpd 應該比 Tomcat 的 pure Java connector 快上很多才對。這可能是所使用的檔案大小導致的結果。Tomcat 會預設把小的靜態檔案 cache 在記憶體中,這可以提供顯著的效能提昇。httpd 在預設情況下沒有作這件事。這很明確地證實 benchmark 的定義會影響結果。

Christopher Schultz(Tomcat mailing list 的常客)以一個較大範圍的檔案大小進行這個測試(譯註:連結已經失效),在我看來這個測試比較好一點。結果顯示在下面這張圖表中:


這更符合預期了,不過有一些有趣的重點要注意:
  • Apache httpd 與 Coyote APR / native 的效能表現在同一個水準
  • Coyote NIO 只落後 httpd 與 Coyote APR / native 一點
  • 看起來似乎 sendfile 的效益有一個極限在。有可能是硬體限制,不過值得進一步關注。我已經把這個加到我的待辦事項中。
  • 對於小於 10KB 的檔案,Tomcat 有顯著的效能提昇。
任何效能測試的細部結果都會隨著一些因素而改變,像是使用何種硬體、同時有多少 request、keep-alive 與 cache 的使用。在特定的測試中,這些因素會改變結果,不過大多數的效能測試結果會保持一樣:
  • httpd 與 Coyote APR / native 有差不多的效能
  • Coyote NIO 比 Coyote APR / native 差一點(在用 SSL 的情況下會差更多)
  • Coyote BIO 通常是最差的。
要設定出最佳的效能,這會隨著 application 的不同而不同。一個 application 的最佳設定取決於 resource 的大小、動態 / 靜態內容的比例、目前的使用者數量、這些使用者的使用模式...... 等等。一般來說,可以透過增加 cacheMaxSize 值、增加可處理 request 的 connector thread 數量(使用 maxThreads)、開啟 keep-alive(用 maxKeepAliveRequests)來增加效能,不過這些都會增加資源的使用量。每個不同的 application 得在效能與資源使用量之間作取捨。

另外要注意的是:Tomcat 中沒有「讓它跑的比較快」的魔法設定。如果你的 application 花 15 秒來產生一個 request,Tomcat 沒辦法改進些甚嘛。你必須監控你的 application 以了解為甚麼 request 會花這麼久的時間,然後適當地調整你的程式碼。

雖然單純從效能觀點,沒有什麼好理由去用 httpd,不過還是有其他論點支持 Tomcat 應該搭配 httpd。最常見的原因是為兩個以上的 Tomcat 提供 load balance。httpd 並不是唯一的選擇,也可以用硬體的 load balance 或是其他 reverse proxy,不過系統管理者仍然會選擇他們已經很熟的 httpd。我未來會寫更多用 httpd 當 load balancer 的文章。

有需要支援 Java 以外的技術,也是 Tomcat 與 httpd 一起使用的原因。Tomcat 可以支援 PHP 與 Perl,不過在 httpd 當中支援度較好。因此,對於需要混合技術的網站,httpd 可以用來當前端的 web server,將 request 導向 mod_php、mod_fastcgi、mod_proxy_http(給 Tomcat)或是其他的 module。

另一個這樣用的原因:httpd 可以將 Windows 的身份認證整合進來。有 Tomcat 整合 Window 身份認證的方式,在更廣泛地使用之後,這讓是不是用 httpd 變得不那麼重要。然後,這仍是讓 httpd 搭配 Tomcat 使用的一個常見原因。

總而言之,有充分的理由讓 httpd 搭配 Tomcat 使用,不過靜態內容的效能並不是其中之一。如果你僅僅是為了加強靜態內容的效能而用 httpd,那我建議你看一下給 Apache Tomcat 使用的 Coyote APR / native connector。

2012年6月17日 星期日

GWT 的 Super Dev Mode 是怎麼運作的?

原文網址:http://tbroyer.posterous.com/how-does-gwts-super-dev-mode-work

GWT 的 Super Dev Mode(別名:Super Draft Mode)一個月前第一次 tease,然後持續發展到上個禮拜正式 land,for your testing pleasure.(譯註:翻譯不能 Orz)

這個 Dev Mode 強化了哪些東西?

在回答這個問題前,讓我們先回頭了解一下 Dev Mode 的構造,它是由 browser plugin 與 code server 所組成、用 TCP 的 socket 彼此溝通以在 JVM 中執行你的 GWT 碼然後跟 browser 互動(以及執行 JSNI),而不用先跑一次漫長的 compile 過程。GWT 的 Dev Mode 也可以(額外)嵌入一個 servlet container(預設是 Jetty 6,也可以用其他的),所以它可以執行 server 端的程式碼。在 GWT 開發指南中可以找到這張圖:
Dev Mode 其中一個優點是在 JVM 中執行你的 client 端程式碼,你可以使用標準的 Java debugger,有 breakpoint、可以觀察變數......等等。此外,server 端的程式碼也在同一個 JVM 上頭執行,就更容易 debug(不過只適用於小程式)。缺點就是需要一個 browser plugin(它得在 UI thread 中阻斷 JS 的執行,但是同步的 XMLHttpRequest 沒辦法提供夠好的效能、Dev Mode 又很聒噪地一直發,更不用說同步的 XHR 將要被淘汰了。)

2012年6月9日 星期六

GWT 已死!

原文網址:http://www.guynirpaz.com/2012/04/28/gwt-is-dead/
翻譯:Alice Liu
校正:PsMonkey

GWT 已經死了啦... 至少對 Totango 來說是這樣的。我們正處於將所有 Totango UI 轉換到 native HTML5 的最後階段。我知道這對於有在關注前端 JavaScript framework 的人而言可能不是什麼新聞,但是在過去幾個月中,我被問了好多次關於使用 GWT 的看法,現在我要清楚表明——我不推薦任何人開始使用 GWT,因為這是個沒有未來的 project。

2012年5月26日 星期六

App Engine 1.6.6 版發佈

原文網址:http://googleappengine.blogspot.com/2012/05/166-sdk-released.html

我們實在太興奮了,迫不及待分享 Search API 這個功能;不過,五月份我們還是有正規的版本發佈時程,裡頭有滿滿的新功能、錯誤修正、以及對一些小東西作粹鍊。

Search API

配合 Search API,我們在 Admin Console 的畫面中加入一個目前仍在實驗階段的 quota 額度限制。我們也修正一些 bug、改變一些 API 的命名以增加一致性。

service 改進

因為不再支援 Master/Slave datastore,新的使用者將不能建立 Master/Slave application。我們強烈建議開發者將現有的 Master/Slave application 轉換到 HRD 上

功能更新

我們一直找尋解除系統限制的方式,讓開發者可以從 App Engine 平台中獲得更多。在這個版本中,開啟付費功能的 application 現在可以設定多達 100 個 cron 工作。(謎之聲:還不是只爽有付錢的人...)

最後,我們每週將會提示一小部份的 admin console 使用者參加使用者滿意調查。

當然,我們沒辦法把所有要告訴你們的東西都放上來,所以請不要忘記閱讀完整的版本更新紀錄(PythonJavaGo 版)。Stack Overflow 的 App Engine 板是很棒的地方,可以讓你從社群、App Engine 團隊中的成員得到技術上的協助,我們也持續地在 Google Group 上討論 App Engine。

譯註:文末還有一段,是 Google App Engine 正在找尋 Solutions Architects

2012年5月4日 星期五

Hibernate + HSQLDB 炸裂

測試環境:Hibernate 4.1.2、HSQLDB 2.2.8、Java 1.6

抓了兩個 H 的最新版,結果前後炸了四五個小時才能開始塞資料。茲紀錄如下。

大抵上是參考 《Hibernate 學習筆記》 (中文嘛...... [毆飛]),只不過 database 改成用 HSQLDB Server 版。

第一個遇到的問題是:只要一作 session.beginTransaction(),Hibernate 就會炸「No suitable driver found for jdbc:hsqldb:hsql://localhost/DBNAME」,最後找到的解法是在建立 Configuration 之前就先執行「Class.forName("org.hsqldb.jdbc.JDBCDriver")」。(參考資料:Hibernate forum

順便補一下,似乎在 Hibernate 4 以後 Configuration.buildSessionFactory() 就被 deprecated 掉,不過(到目前為止)使用起來還是正常。至於標準寫法我沒研究,可參見 stackoverflow

第二個遇到的問題是 session.save() 時 HSQLDB 會炸「user lacks privilege or object not found」,據說如果用 Hibernate 3.6 跟 HSQLDB 1.8 就不會有這種困擾...... Orz。這篇提到要將 Hibernate 的 hibernate.hbm2ddl.auto 設定為 create,實際上要設定成 update。參數之間差別如下:(參考資料:Hibernate reference
  • create:系統 init 時會作檢查 table 是否存在,有的話就會 drop 掉重建。
  • validate:只有在 table 存在時,系統才能正常 init。
  • update:理想狀況,init 時沒 table 會建,有 table 就續用。(不過如果系統運作中有外力 drop table,則會出錯)

做了這些手腳之後,目前 save() 之後東西都會乖乖進資料庫了。於是又寫一篇可能沒多久之後就沒意義的 debug / 教學文...... 軟體業阿...... [遠目]

2012年5月1日 星期二

GWT Elemental library

原文來自於 +Ray Cromwell 以及後續某些 comment。

特別感謝 +YC Ling 指正 bleeding edge 用法。

Ray Cromwell:
為甚麼 GWT 的 SuperDevMode 還沒有 commite 到 trunk?幾個星期前,我提到一個 Google 內部、給 GWT 用的 library,簡稱「clevername」。由於種種原因,目前 SuperDevMode 跟 Google 內部的東西產生了一些相依性,其中一個就是「clevername」。

2012年4月28日 星期六

App Engine 1.6.5 發佈

原文網址:http://googleappengine.blogspot.com/2012/04/app-engine-165-released.html

(譯註:省略帶有詩意但是沒啥意義的第一段)

Datastore

我們在 Datastore 導入了一個實驗階段的 query 方式:projection query。對於 SQL 愛好者而言,可以用類似下列方式作 query:
SELECT Property1, Property2 FROM MyEntity ORDER BY Property3
projection query 與 keys-only query 的運作成本、效能等特性都相同,但是回傳的 entity 物件只有指定要求的 property。

2012年4月16日 星期一

自製 GWT 版 GAE Channel API

雖然之前寫〈用 Channel API 實作簡易聊天室〉,已經算是把 Channel API 順過一遍了,但問題是......
有了 GWT,誰還想去碰 JS 啊...... 囧>
於是 google 了一下,發現這兩個 project:gwt-gae-channelgwt-channel-api 有將 Channel API 包好給 GWT 用,不過各有優缺點,茲簡單分析如下:

2012年3月30日 星期五

區分 GWT 程式碼的執行環境

這是在勁過呂布的噗浪上遇到的問題。

起因是傳給 String.split() 的 RegExp,Java 與 JavaScript 的寫法相似、但不是完全相同(參見 GWT 文件),也就會遇到「development mode 下正確、但 compile 之後得不到預期結果」的窘況。再者,如果 client/server 端都用 Java 寫,也會擴大這個問題的麻煩度。

這時候我們需要 GWT

(謎之聲:你不就在寫 GWT 了 [指])

這裡是指 com.google.gwt.core.client.GWT。這個 class 提供 isClient()isScript()isProdMode() 理論上與 isScript() 等意)這兩個 static method。

isScript() 是辨別程式碼是以 JavaScript 的方式執行、還是以 Java 的方式執行。在 development mode 下會回傳 false,在 compile 時才由 GWT compiler 改成回傳 true。

isClient()(邏輯上)不會區分是不是 development mode,純粹辨別程式碼是在瀏覽器上頭執行,還是在 JVM 上頭執行。

理解這種文字遊戲有點麻煩,還是來寫個程式比較實在。首先是 client 端的程式:
public class Foo extends Composite
 public static final RpcServiceAsync rpc = GWT.create(RpcService.class);
 private VerticalPanel panel = new VerticalPanel();
 public Foo(){
  String result = "[client] GWT.isClient() = " + GWT.isClient()
    + ", GWT.isScript() = " + GWT.isScript();
  panel.add(new Label(result));

  rpc.gwtMethod(new AsyncCallback<String>() {
   @Override
   public void onSuccess(String result) {
    panel.add(new Label("[server] "+result));
   }
   
   @Override
   public void onFailure(Throwable caught) {}
  });
  
  this.initWidget(panel);
 }
}

而 Server 端 RpcServiceImpl 的 method 長這樣:
 public String gwtMethod() {
  return "GWT.isClient() = "+GWT.isClient()
   + ", GWT.isScript() = "+GWT.isScript();
 }

在 development mode 下,會得到:
[client] GWT.isClient() = true, GWT.isScript() = false
[server] GWT.isClient() = false, GWT.isScript() = false
實際 deploy 到 web server 上,會得到:
[client] GWT.isClient() = true, GWT.isScript() = true
[server] GWT.isClient() = false, GWT.isScript() = false
如此一來,雖然遇到了抽象滲漏的現象,但我們還是可以藉由  GWT.isClient()GWT.isScript() 判斷是 development mode 還是 production mode、是 client 端還是 server 端、給予對應 RegExp,來解決這個問題。

2012年3月28日 星期三

App Engine 1.6.4 發佈

原文網址:App Engine 1.6.4 Released

App Engine 三月份持續發佈塞滿新功能、系統改進與錯誤修復的新版本。在我們朝 Google I/O 前期議程邁進的同時,我們也持續專注在使產品更光鮮亮麗,這個釋出的版本就是一個閃閃發光的例子。

系統面的改變

  • Log:現在新的 log 儲存設定可以是一個月,而超過你指定界線的 log 將會被刪除。
  • Datastore Index 統計資料:Admin Console 的 Datastore Statistics 頁面現在會在 Datastore Entity 後頭顯示 Index 所佔用的儲存空間量。
  • Blobstore 遷移功能:Datastore Migration 工具現在包含一個實驗階段的選項,可以讓你在 M/S 與 HRD 之間搬移 Blobstore 物件。我們強烈建議所有的 application 都改用 HRD。
  • Datastore 備份至 Google Cloud Storage:在 1.6.3 版中,我們推出了 Blobstore 的備份還原功能:而在這個版本中,我們增加將資料備份到 Google Cloud Storage 的功能。
  • Memcache Viewer:我們導入了一個功能,可以觀看 memcache 的統計資料、以及輸入 key 值來查看對應的 memcache entry。
  • 從 Google Cloud Storage 提供資料:現在不但可以從 Blobstore 提供 blob,也可以由 Google Cloud Storage 直接提供。

Runtime 的改變

  • Thread:Java 與 Python 都提供執行 backend 時有 background thread 這個實驗階段的功能。此外,我們讓 Java 也能像 Python 2.7 一樣在處理 frontend request 時可以使用 thread。

Datastore framework 的改變

  • Python 的 NDB:NDB API 已經脫離實驗階段,成為完整支援的功能。這個新一代的 Datastore API 加強資料模組化、查詢、並從裡到外地支援非同步計算。
  • JPA 2 與 JDO 3:我們已經針對 App Engine 的 DataNucleus plugin 做出顯著的改善。這個實驗中的 plugin 版本為 2.0,增加 JPA 2、JDO 3 的支援,以及超過 40 個錯誤修正。這裡有列出完整的版本更新紀錄。

上面這些還不是全部,你可以在版本更新紀錄中(Python 版Java 版)得知所有的新功能與錯誤修正。請將你所有的回饋意見送到我們的 Google Group;如果你有什麼程式方面的問題,可以在 Stack Overflow 上找尋來自我們或其他優秀開發者的協助。

2012年3月1日 星期四

App Engine 1.6.3 發佈

今年的第二個版本,讓你可以馬上開始使用這些新功能。有什麼可以比在你的 application 支援 A/B Testing、或是從你的 Google App domain 寄信時使用 DKIM 簽章,還更令人興奮呢?(謎之聲:降低收費啊!笨蛋!)這個版本有許多令人興奮的變化,讓你在今年多出來的這天保持忙碌。

2012年2月29日 星期三

GWT 繁體中文徵稿計畫

徵稿主題:
  • core GWT
  • GPE
  • 3rd party library(不含 GXT、SmartGWT 此類 library)

2012年2月14日 星期二

GWT、GAE 的日期問題

開發環境:GWT 2.4.0、GAE 1.6.1、GPE 2.5.1、Objectify 3.0

正在作一個預約系統,很自然會有「取出某天所有預約」之類的需求,所以將 class 設計成這樣,期望能節省一些麻煩:
class Booking{
    Date date;
    int hour;  //時間間隔以「小時」為單位
    //skip other field
}

由於 GAE 的 Datastore 理論上沒有提供在 query 時可以卡入日期函數,所以如果不對日期作處理,下面這段「取得今天的預約」會完全取不到任何東西:
Objectify ofy = ObjectifyService.begin();
ofy.query(Booking.class).filter("date", new Date()).list();  //empty list

2012年2月9日 星期四

Cache 教學手冊 [上]

原文網址:http://www.mnot.net/cache_docs/
譯註:原文擷取時間為 2011.12。原文內有許多內部連結,因 blog 特性的關係,故改連結至原文段落。

Cache 教學手冊
給網站作者與網站管理者
這是一個 informational document。雖然本質上是技術性文章,但它試圖讓你理解現實狀況下所涉及的概念。因此,為了保持文章脈絡的清晰,會簡化或省略某些內容的觀點。如果你對這些主題的細節感興趣,可以參考「延伸閱讀的參考資料」。

2012年2月1日 星期三

App Engine 1.6.2 版發佈

原文網址:http://googleappengine.blogspot.com/2012/01/app-engine-162-released.html

有些人可能會把龍想像成兇殘、囤積寶物、會噴火的怪獸。但 App Engine 團隊將龍視為吉祥與好運的象徵,我們很高興發佈龍年的第一個版本。

2012年1月18日 星期三

Shadow DOM 到底是什麼鬼?

原文網址:http://glazkov.com/2011/01/14/what-the-heck-is-shadow-dom/

當你建構網站時,可能會用到一些 JavaScript library 。這樣的話,你也許要感謝一下將這些 library 設計的不是那麼糟糕的無名英雄們。

這些網路上的英勇戰士要面對的一個常見問題就是「封裝」。你知道,這個概念是物件導向編程的重要基礎之一,現代軟體工程大多數建立在此之上。你要如何在「你寫的程式碼」與「使用『你寫的程式碼』的程式碼」之間畫出界線?

先屏除 SVG 不提,現在的網路平台僅提供一種內建機制來將程式碼各自分開,但是這種作法並不漂亮。沒錯,就是在說 iframe。對於大多數封裝的需求來說, frame 實在過於笨重而且有很多限制。
你要我必須把每一個自定的按鈕放在不同 iframe 區塊裡是什麼意思?你是哪來的神經病?
因此,我們需要更好的東西。結果就是,大多數瀏覽器都已經悄悄採用了強大的技術來隱藏其中慘不忍睹的實作細節。這種技術稱為「Shadow DOM」。

我姓 DOM,叫做 Shadow DOM

「Shadow DOM」論及瀏覽器的一種能力,這個能力會讓 DOM element subtree 包含於 document 的呈現內容裡,但是卻不會在 main document DOM tree 裡。

仔細地來想想一個簡單的 slider:
 

把上面這段程式碼貼到 html 檔,隨便用個 WebKit-powered 的瀏覽器開啟,就會看到畫面如下:



還蠻單純的吧。一條滑動軌道(slider track)、一個可以沿軌道滑動的控制軸(thumb)。

等等,什麼?input element 裡面竟然有可以單獨移動的 element?為什麼我用 Javascript 卻找不到它呢?
var slider = document.getElementsById("foo");
console.log(slider.firstChild); // returns null
這是某種魔法嗎?

當然不是,我誠實公平的鄉民們。那是 Shadow DOM 造成的。你看,瀏覽器開發人員意識到,完全用人工編寫 HTML element 的外觀和行為是吃力不討好的。所以他們算是對你使了障眼法。

他們在網頁開發人員的可操作範圍和細節實作面之間劃了一條界線, 讓你無法接觸到細節。然而瀏覽器卻可以跨越那條界線。有了這條界線,他們就能照常使用過去喜歡的 Web 技術來建立所有 HTML 元素,擺脫一堆「div」和「span」的世界。

有些例子單純一點,像剛剛提到的 slider。有些則相當複雜。我們來檢視一下「video」元素。它有播放按鈕,時間軸,滑鼠移入才會出現的音量控制區,如下:



你看到的全都只是 HTML 和 CSS - 藏在 Shadow DOM subtree 裡面。

引用 magnetic meme duo 的其中一句歌詞,“他到底是怎麼辦到的?” 為了建立更理想的推演模型,我們先假設有辦法透過 Javascript 控制它。給定這個簡單的頁面:
<html>
<head>
<style> p { color: Green; } </style>
</head>
<body>
<p>My Future is so bright</p>
<div id="foo"></div>
<script>
    var foo = document.getElementById('foo');
    // 注意!下面這個不是真實的 API
    foo.shadow = document.createElement('p');
    foo.shadow.textContent = 'I gotta wear shades';
</script>
</body>
</html>
我們實際得到的 DOM tree 如下:
<p>My Future is so bright</p>
<div id="foo"></div>
可是它彷彿呈現這個樣子:
<p>My Future is so bright</p>
<div id="foo"> <!-- shadow subtree begins -->
    <p>I gotta wear shades</p>
</div> <!-- shadow subtree ends -->
或者看起來像這樣:



看到了嗎,是如何讓第二個句子不是綠色的?因為在這個網頁裡面,我給定的 p selector 無法指到 Shadow DOM 裡面。你說這有多酷阿?Framework 開發人員有了這種能力可以產出什麼樣的東西呢? 可以讓你寫 widget 而不用擔心隨意一個 selector 會干擾你的 style ,這種能力看起來.....簡直令人為之著迷。

傳遞 Events 的過程

自然而然的,網頁內可以偵聽到 Shadow DOM 裡被觸發的事件。例如,如果您點選 audio 元素裡的靜音按鈕,裝 audio 的 div 內會有 event listener 偵測到這個 click 事件:
<div onclick="alert('who dat?')">
    <audio controls src="test.wav"></audio>
</div>
然而,如果想要找出是誰觸發這個事件,你將會找到 audio element 本身,而不是它裡面的按鈕。
<div onclick="alert('fired by:' + event.target)">
    <audio controls src="test.wav"></audio>
</div>
為什麼?因為當事件被傳送經過 Shadow DOM 邊界時,傳送目標會被 re-targeted 以避免 Shadow subtree 內的細節曝光。這樣一來,你能捕捉到由 Shadow DOM 發出的 Event,但是實作這個功能的人仍然可以對你隱藏這些細節。

用 CSS 操作 Shadow 樣式

另外有件事情是藏有玄機的,那就是能否用 CSS 操控 Shadow DOM subtree 樣式的能力?又該怎麼作。假設我要自己定義 slider 的外觀。代替標準的 OS 特定外觀,我想讓他變得更有型,像這樣:
input[type=range].custom {
    -webkit-appearance: none;
    background-color: Red;
    width: 200px;
}
得到的結果是:



好,目前看起來還不錯,但是我接下來要怎麼裝飾控制軸(thumb)呢?目前已經確定我們一般使用的 CSS selector 是無法指到 Shadow DOM tree 的內部元素。原來,有便利的 偽屬性 能力,使得 Shadow DOM subtree 可以讓任意的偽元素辨識器和 subtree 內的元素產生關聯。例如,在 WebKit 瀏覽器上要指到控制軸(thumb)的樣式可以這麼做:
input[type=range].custom::-webkit-slider-thumb {
    -webkit-appearance: none;
    background-color: Green;
    opacity: 0.5;
    width: 10px;
    height: 40px;
}
就會看到如下圖:



是不是很棒?想想看,您可以裝飾 Shadow DOM 內的元素而不用實際的去存取它們。而且建立這個 Shadow DOM subtree 的人也能決定哪些部份可以被裝飾。當你在打造 UI Widget 工具包的時候,難道不想擁有這樣子的權力嗎?

Shadow 有 API 接口,這對瘋狂科學家們有什麼意義?

談到很棒的能力,我們可以來看看替 shadow DOM subtree 添加一個元素會發生什麼事情?下面做個實驗:
//建立一個帶有 Shadow DOM subtree 的元素。
var input = document.body.appendChild(document.createElement('input'));
//加入一個子元素。
var test = input.appendChild(document.createElement('p'));
//..寫一些文字。
test.textContent = 'Team Edward';
顯示如下:



哇嗚!歡迎來到 twilight DOM - 在畫面上看不見,但是會在 HTML DOM 出現的網頁區塊。它有什麼用處嗎?不是很確定。不過你想要就可以用。年輕人似乎喜歡這種 fu。

但是如果我們確實有辦法像 Shadow DOM subtree 的一部分那樣顯示 element 的 children 又能作什麼?把 Shadow DOM 想像成一個有 API 的樣板,透過 API 你可以看得到 element 的 children:
//注意:以下是虛擬碼,不是真的API。
var element = document.getElementById('element');
///建立一個 Shadow subtree。
element.shadow = document.createElement('div');
element.shadow.innerHTML = '

Think of the Children

' +
{{children-go-here}}
'; // Now add some children. var test = element.appendChild(document.createElement('p')); test.textContent = 'I see the light!';
因此,如果你 traverse DOM 一遍,你會看到:
<div id="element">
    <p>I see the light</p>
</div>
但是畫面上顯示的卻是:
<div id="element">
    <div> <!-- shadow tree begins -->
        <h1>Think of the Children</h1>
        <div class="children"> <!-- shadow tree hole begins -->
            <p>I see the light</p>
        </div> <!-- shadow tree hole ends -->
    </div> <!-- shadow tree ends --> 
</div>
幫 element 這個 div 增加 children 之後,如果你在 DOM 中觀察這些 element,看起來就跟平常的 element 沒兩樣;但是在呈現時,他們會被傳進 Shadow DOM subtree 的 API 接口裡。

2012年1月8日 星期日

改善 array 的記憶體使用方式

原文網址:https://plus.google.com/u/0/110412141990454266397/posts/U7wMJv2cNnj

本週 GWT 改良進度:加強 array 的記憶體使用方式。

GWT 會給予每個 type 一個稱為 queryId 的數字,透過檢查這個數字來模擬出 Java 的 runtime type check。舉例來說,這段程式碼「foo instanceof IFoo」會去取得 IFoo 的 queryID,假設是 42。

接著,在設定 object 的 prototype 時,會加上 map,例如:
Foo.prototype.castableTypeMap = { 42: 1 };
如此一來,像「foo instanceof IFoo」會轉換成「!!foo.castableTypeMap[42]」,在這個例子會回傳 true。

一切都運作完好...... 除了 array。GWT 中模擬 array 的方式,並不是透過修改 Array.prototype,而是在 native 的 JS Array object 當中加入 field,所以「x=new Foo[10]」最終會變成像這樣:
x=makeArray({42:1});
發現問題了嗎?每一次 allocation 會使用一個新的 array literal,即使它們共用相同(且 immutable)的 literal。這會浪費 JS VM 的記憶體。

2012年1月6日 星期五

更改 gwt.xml 需 refresh 兩次才見效

測試環境:GWT 2.4.0、Eclipse 3.7、GPE 2.5、GWT Developer Plugin 1.0.9738(Chrome)

今天輾轉發現 gwt.xml 導致的奇妙問題:
在 Development Mode 下,修改 gwt.xml 的內容,browser 第一次 refresh 依然保持原設定,第二次 refresh 才會呈現修改後的結果。
已知會造成這個現象的是 <stylesheet> 以及 theme 的 <inherits>。<entry-point> 不會有這個問題,其餘未測。

由此延伸出這些測試:
  • 如果單純修改 <stylesheet> 的 CSS 檔案、在第一次 refresh 就會呈現修改結果。
  • 以 Chrome Developer Tools 觀察:<stylesheet> 的 CSS 檔案以及 theme 對應的 CSS 檔案,都是由 foo.nocache.js 所呼叫
  • 修改 client 下的 .java 檔,在第一次 refresh 就會呈現修改結果......
最大的爆點還是在於為甚麼是「第二次」呢?需要重開 Development Mode 之類的行為才會正確,還比較合邏輯些?GWT 的 code server 到底做了什麼啊啊啊啊....... <囧>

ps. 雖然有點多餘,不過 Mac 跟 Windows XP 的測試結果一樣......