訂閱

上次更新

May 25, 2015 12:00 AM

所有時間皆為協調世界時(UTC)。

Powered By

Planet

摩茲星球 | MozTW Planet

這邊是工作人員碎碎念的地方,您可獲得最新出爐的資訊以及最不成形的想法 :P

May 18, 2015

Othree

TypeScript, AtScript, ES Decorator

AtScript

前陣子花了些時間研究了 TypeScript 和一些相關的發展,包括了 Google Angular Team 的 AtScript 和推進 ES 標準的部分,會開始感興趣深入研究主要是因為 Angular 2 說改用 TypeScript 寫,好奇為什麼會有這樣的發展才下去搜尋資料的,這篇文章算是記錄用的,不過其實離寫好已經一陣子了,因為剛好遇到 Modern Web Conf,想說拿這題目去分享,就讓文章晚點上線了,後來投影片還有補充些內容,這篇文章就沒再更新了,所以兩邊會有些差異就是~

ECMAScript 標準一直以來都是動態型別的,雖然資料有不同的型別,但是變數本身是沒限制型別的,而在 ECMAScript 發展的過程中,靜態型別第一次出現是在已經被廢棄的 ECMAScript 4 裡,網路上還可以找到一些資料,可以看看當時設計的語法,和現在常看到的 :type 的寫法很接近,後來這個設計也在 ActionScript 3 中被使用,微軟現在的 TypeScript 也是用這種寫法。那加入靜態型別的特性會有什麼好處呢,我認為有兩個主要的優點,第一個是可以讓程式碼更可靠,減少一些 bug 發生的機會,對於大型專案來說,多了這個限制的差距是蠻大的,另外一個優點則是 JS Engine 更好最佳化,以前也有提過現在的 V8 引擎就已經會判斷變數的型別會不會有變化來做最佳化了。

或許是因為微軟對於大型專案開發的關注比較多吧,他們於 2012 年推出了 TypeScript,為 JavaScript 加入了靜態型別,用的語法很簡潔:

var i:int;
var message:string;

另外還提供了當時沒有的 class 和之前提過的定義檔等東西,TypeScript 一開始是基於 ECMAScript 5 設計的,不過在 ECMAScript 6 差不多定案後,微軟也開始著手把 ES5 based 改成 ES6 based,像是 class 就會改用 ES6 原生的,而 TypeScript 所提供的靜態型別檢查功能其實是靜態分析而已,也就是只有在把 .ts 檔案編譯成 .js 檔案時會做檢查,而由於 JavaScript 還沒有 type 的特性,所以這些型別的資訊其實在編譯過後都會被拿掉。目前除了 AngularJS 2 改用 TypeScript 之外,還有像 Asana 和 Mozilla 的 Shumway 都是用 TypeScript。

Google Angular Team 似乎對此還不夠滿足,因此他們開始發展 AtScript,在 TypeScript 上再加入 annotation 的功能,名稱的 At 代表的是 @ 這個符號,因為這個符號是很多語言寫 annotation 用的符號,自然 AtScript 也是用這個符號來標記 Annotation:

@Component({selector: 'foo'})
class MyComponent {
  @Inject()
  constructor(server:Server) {}
}

Annotation 簡單翻起來也是註解,不過他和 comment 不一樣,不是給人看,而是要給 compiler 和 JS engine 看的,而且實際上也會影響程式的一些運作,annotation 應該是一種完全沒有也不影響程式執行的 metadata,不過細分下去應該可以分為兩類,第一種是 Java 的 annotation,以 metadata 為主,像是物件的角色、物件間關係等,另外一種則是 decorator annotation,可以讓函數加上各種不同特性,其實就是 decorator pattern 的簡易語法,看到一些範例當中,最讓我覺得厲害的就是 memorize 了吧,如果程式引擎支援,加上一行 memorize 的 annotation 就可以讓那個函數自動有 memorize 特性,如果使用不支援此特性的引擎來執行程式,函數的輸出也不會有錯,就是沒有 memorize 的效果,效率會比較差,Python 中就有 lru_cache 這個 decorator 可以做到這樣的效果(Python 的 decorator 語法是提供 syntax sugar,不過寫法和其它語言的 annotation 很像):

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

AtScript 一個很重要的原則是這些附加的資訊,都要在 runtime 可以使用,所以就不像 TypeScript 那樣只是把不支援的東西拿掉而已,像上面費氏數列的程式碼如果改用 AtScript 寫會變成:

@lru_cache()
function fib(n) {
  if (n < 2) { return n; }
  return fib(n - 1) + fib(n - 2);
}

然後用 AtScript compiler 編譯過後會多上一段程式碼做類似下面的事情:

fib.annotations = [
  new lru_cache(),
];

這個 annotations 屬性在 runtime 時就是可以取用的資訊,目前 AtScript 的 annotation 就是比較偏重於 metadata 而不是 decorator,所以這些資料並不會直接讓函數有不同特性,而 AtScript 另外一個新東西 introspection 也是和 runtime 有關,是 TypeScript 所沒有的 runtime 時的型別檢查,JavaScript 要怎樣做執行階段的型別檢查呢?沒錯,基本上就是土法煉鋼,不過 AtScript 是引入一個 rtts(run time type assertion) 的 library 來做這件事,目前主要也是用 Angular Team 維護的 assert.js,本來的 fib 再改寫一下:

function fib(n:number):number {
    if (n < 2) { return n; }
    return fib(n - 1) + fib(n - 2);
}

然後編譯過後大概會變成:

function fib(n) {
  assert.argumentTypes(n, number);
  if (n < 2) {
    return assert.returnType((n), number);
  }
  return assert.returnType((fib(n - 1) + fib(n - 2)), number);
}

可以看到不管是在函數開頭還是要回傳之前,都會多了用 assert.js 做型別檢查的程式碼,當然,多做的這些型別檢查是會造成效能影響的,所以 AtScript 把 runtime 的型別檢查分成兩個階段,開發階段和成品階段,成品階段,要上線的時候,就輸出不包含型別檢查的 js 程式碼,這樣就不會影響效能。AtScript 其實目前沒有自己的編譯器,而是使用 Google 的 Traceur,Traceur 基本上是個 ES6 to ES5 compiler,不過實際上他還多一些非 ES6 標準的語法支援,包括了前面提到的 Type、Annotation,不過使用時要加些參數:

traceur --annotations true --type-assertions --types true fib.ats --out fib.js

ng-europe 研討會,就有一場關於 AtScript 的演講:

裡面除了基本的介紹,為什麼會發展 AtScript 之外,還有很重要的未來發展,Angular Team 是有打算把 Type、Annotation 等等特性推回 ECMAScript 未來的標準之中的。在 ECMAScript 標準的發展上,其實早在之前就有一些變數型別相關的功能在討論,包括了 typeguard,不過都沒有進到目前的 ECMAScript 6(2015),目前 AtScript 和 TypeScript 兩者正在逐漸互相同步,也有共同合作,而且 AtScript 還沒有嚴謹的 spec 文件,所以會看到官方發佈說 AngularJS 2 用 TypeScript 開發,而不是用 AtScript,目前看到 TC39 討論裡面,除了 type 之外,幫其它新東西提出 proposal 的,很令人意外,竟然是 Yehuda Katz,可以看到去年四月的會議記錄就有他提出 decorator 特性的討論,另外 TypeScript 的 Issue 1557 是關於在 TypeScript 中加入 AtScript 的 annotation 支援,Yehuda Katz 也有提到他正在整理相關資料,幾週後會在 TC39 會議提出,在他的 github 帳號上也可以找到相關的資訊,我個人對 Yehuda Katz 評價很高,不過實在是想不太到為什麼會是他跑出來推動這部分的發展,不過總之 Yehuda Katz 打算提出的是比現在 metadata 為主更進一步的 annotation,也就是包含像 Python decorator 特性的 annotation,如果真的順利成案,其實也不知道是好是壞,好的是一些程式碼可以更簡潔,壞的是 JavaScript 語法越來越多,入門要學的東西也變多很多。

May 18, 2015 10:24 AM

May 16, 2015

Othree

TypeScript 過去、現在、未來

今年 Modern Web Conf 的投影片喔,其實整份演講最重要的點就是 type 看來就是會進入 ECMAScript 了。

May 16, 2015 07:03 AM

May 08, 2015

BobChao

Lightning Talk in MozTW Lab

摩茲連續聚 第六集

我們今天在 MozTW Lab 嘗試做了 25 分鐘的 Lightning Talk,效果不錯。

「有 Talk」這件事其實與 MozTW Lab 原本的設計理念不太一致:本來,MozTW Lab 就是個「來我家寫程式」的活動,大家應該來做自己的事情就好,而有事情可以互相幫忙。不過有些理由,讓我覺得這樣的分享時間或有必要:

  • 現在每週 MozTW Lab 都有超過 20 位參與者(到我寫這段文字的 22:11 為止,在場仍有 17 位社群成員各自忙著,Simply Amazing),大家卻不見得瞭解對方在做什麼,甚至也叫不出名字,這讓「有事情可以互相幫忙」的機會降低了不少。
  • 我們的連續聚幾百年沒辦了,大家彼此少了很多可以快速介紹一些 Web 相關議題的機會。
  • 我還是希望來參加 Lab 的夥伴能有機會更認識 Mozilla 的各種專案與想法

於是上週在 Telegram 上喊了下,本週也順利做了第一次實驗。主要設計概念是:

  • 不能變成活動主題
  • 時間不能太長 - 目前設定每次 Lab 花至多 30 分鐘時間分享,因為絕大部分時間應該還是要讓大家來做事。
  • 試行彈性時間:不一定每場剛好 5 分鐘,超時會催場,但不會強制拔插頭。
  • 鼓勵大家分享最近在做的事情

稟持這樣的想法,第一集閃電秀就在今天熱鬧演出了!這一集的內容有:

  • 某齧齒動物跟大家分享了報社的兩三事,恕我不能講更多...
  • 新朋友 Ross Ziegler 與大家相見歡
  • Irvin 介紹了最近的熱門議題:Facebook Zero 與網路中立性
  • 則介紹了一下最近 Webmaker.org 的動態

那,下週呢?下週就看你的了,現在就上網預約報名吧!戰神表示下週會希望能夠開直播,請關注 MozTW 的粉絲頁以獲得進一步消息囉!

by Po-chiang Chao (noreply@blogger.com) at May 08, 2015 04:21 PM

May 02, 2015

Othree

Aster 與 PostCSS

台南

前端為了 performance 需求,把網站推上 server 時會需要把 JavaScript、CSS 之類的文字檔案合併和最小化,如果開發時寫的是 CoffeeScript 或是 SASS 之類的還需要先轉成 JavaScript 和 CSS 這些主流格式,要做這些動作其實第一個想到的是可以用 Makefile,優點是常見、各平台都有,不過寫起來並不像這幾年流行的 build tool 那樣直覺,而前端領域流行的主要是 GruntGulp 這兩個,兩者之中我個人是比較喜歡後起的 Gulp,不過前陣子意外發現一個看起來超正確的 build tool,叫 aster,是 Ingvar Stepanyan 做的,他在 Zurich 的 Frontend Conference 2014 的演講算是比較大規模的發佈:

Aster 這個名稱的來由是 AST,Aster 的特別之處就在於他所有的修改都是在 AST 上做修改:

Aster

相較於 Gulp:

Aster

和 Grunt:

Aster

很明顯的是正確許多的設計,理論上 parse 成 AST 之後,一口氣在 AST 階段把需要的變更做完,然後再用 code generator 產生成果的程式碼,應該省去很多來回轉換的工作,不論是效能還是程式碼處理的正確性都應該是最好的,不過這看似正確設計下的產品,卻沒什麼人用,我嘗試使用過程中還發了幾個 PR 回去,結果到現在都還沒有回應,有種是不是作者都已經放棄的疑問。

然後這兩天,又看到一個其實已經出來蠻久的 compile to CSS language,叫 PostCSS,號稱比 SASS 之類的快上 3-30 倍,還可以做到一些 SASS/LESS 做不到功能,也有很多大企業採用,像是 Google、VK、Alibaba 之類的,應該就是目前最夯的 CSS 工具,然後我看介紹的投影片看到這張:

PostCSS

覺得,這根本和 Aster 是一樣的想法,不過一個處理 JavaScript,做 ES6 to ES5、concat、minimize 甚至是細微的程式碼修改,另外一個處理 CSS,也是做類似的工作,為什麼結果發展的差距會這麼大?想了一陣子之後,我覺得大概狀況是,SASS、LESS 之類的工具目前還是會造成一些讓開發者痛苦的問題,像是 nesting rule 讓 selector 太長太複雜、mixin 和 include 的混淆等等,所以開發者還會一直嘗試不同的新工具,甚至自己開發來解決這些問題,而相對於 CSS,JavaScript 的相關工具比較沒這些問題,二來 JavaScript 的 AST 比 CSS 的 AST 要複雜許多,要參與工具的開發難度是高上許多,Aster 作者的功力還蠻強的,除了 AST 操作之外,他的整個流程控制還用上很先進的 RxJS,其實也算是增加了參與的難度吧,而我想就是這沒有強烈需求加上進入難度高兩點,結果讓 Aster 並不太受到人注意,沒什麼人參與,開發的完整度不夠,我實際用起來,就覺得目前還只是核心完整,但是真的要離實用到專案上還有些距離,加上不知道還有沒有要繼續發展下去,覺得實在很可惜。

May 02, 2015 03:35 AM

May 01, 2015

MozLinks-zh

前進吧,摩茲巴士!Web 工廠的遊牧之旅

在 2013 年 9 年 19 日,Mozilla Japan 宣布 MozBus Project (摩茲巴士)計畫正式啟動。

編按:關於本計畫一年半來的成果,歡迎查看 MozBus 計畫網站

以下是這項計畫的目標:

  • 在任何時間、地點與情境下向一般民眾推廣 Web 知識
  • 研究以 Web 作為災害防治基礎建設的應用
    • 在災難發生時,應用巴士上的車載網絡
    • 預計這個災害防治基礎建設計畫將擴大到全亞洲,而 MozBus 將成為計畫的網絡和資訊中心樞紐
  • 舉辦各種巡迴工作坊
    • 使用 Webmaker 來教導「Web」
    • 舉行網頁和自造者(如雷射切割)工作坊
  • 建置資訊技術教學相關材料
  • 在 Web 的使用、體驗和建構上找尋新的可能性

為了實現上述目標,我們與來自各個領域的企業和組織合作,推出 MozBus 計畫,協同來自各單位的輔導員 (Mentor) 共同進行研究、執行上述的活動。

MozBus 車上設有一台發電機、網絡設備、電腦以及其他必要設備;不管在任何地方、任何情況之下,都能夠隨時運用。藉此,我們也降低了舉辦 Webmaker、網站和創作工作坊等活動的硬體要求。

隨著巡迴工作坊的舉辦,我們期望能開展各項研究和開放實驗專案,並以以公開與共享的方式展現計畫成果,讓其得以被重複使用。

為了維持公正與公開,這些專案將會在 Mozilla Factory 的組織架構之下執行。管理和研究所需要的花費和設備費用,將由出資贊助及參與計畫的企業負擔。

目前,以下大學、研究機構以及企業已參與 MozBus 計畫,包含(截至 2013 年 9 月 19 日):

  • 慶應大學 SFC
  • 日本 OpenStreetMap 基金會
  • 日本國立天文台(The National Astronomical Observatory of Japan)
  • IPSTAR
  • Cisco Systems

MozBus 由 Mozilla Factory 社群共同設計。我們希望透過 MozBus,提供大家體驗和認識 Web 的機會,探究與型塑 Web 的更多可能!

原文 / MozBus Project (en) — Started MozBus: a nomadic Web factory
刊載日期 / Sep 20th, 2013

φ 交大服務學習 chiaoju、mulnihsu 翻譯 - Irvin 編輯

by Irvin Chen (noreply@blogger.com) at May 01, 2015 01:11 PM

April 25, 2015

Othree

React.js Conf

今年的 React.js Conf 雖然是單一 library 的研討會,不過議程的水準卻是蠻高的,而且蠻多內容並不是只限於 React.js,加上也是對於前端領域一些比較新的概念,所以我覺得是這兩年最讓人感到印象深刻的一場研討會了吧,下面介紹幾場我覺得收穫比較多的講題:

React Native

React Native 是 React.js 最近最重大的發展,把本來抽出來的 DOM 底層換成 native app 的操作,原來寫 JavaScript 的開發者就可以直接用寫 React.js App 的方式來做原生介面的 Mobile App,發表時讓很多人眼睛一亮,沒想到會把 React.js 的 Virtual DOM 這樣用,而表現出來的效果也很讓人滿意,因為是用原生的控制元件,也不會有額外的親和力問題,加上現有的很多 JavaScript Library 都可以用,也不會有太多東西要自己從頭開始,Apache Cordova 的 plugin 也都可以使用,最近也正式對外公開了,有興趣的可以試試看。

CSP

之前我也有介紹過的 CSP,在 React.js Conf 也有講者介紹,雖然我覺得現在用 js-csp 來做 Channel 溝通還不是很好用,不過應該 async function 可以用之後會比較普遍吧,就算之後沒真的派上用場,要是轉行寫 Go 也會用的上才是。

Immutable

Immutable Data 已經聽說很久了,不過這場演講算是讓我第一次真的能有些體會到底優點在哪,雖然 Facebook 有 JavaScript 的 Immutable library ,不過這場主要是在介紹資料結構的特性,而不是在介紹自家 library,如果對他們底層如何實做的話還可以看看 Polymatheia 的 Understanding Clojure's Persistent Vectors 系列文章,單純要實做 Immutable Data 並不難,不過要考慮到不對效能產生影響其實還要考慮蠻多細節的,我後來也嘗試實做了一個 Immutable Quad Tree,做的過程中則更實際的體會到所謂的只要簡單的 == 比對就可以知道資料是否相同的好處。另外還要補充一點,Immutable Data 可能在下一版的 ES7(2016) 也會有,總之又是個先學起來不吃虧的。

AST

這場演講是在講修改 Abstract Syntax Tree 來做一些程式碼的修改,算是把 AST 的應用講得很淺顯易懂的一場演講,還順便介紹到很多相關的工具,像是 estools,其實一個程式語言的 spec 通常只有定到語法的部分,後面 compiler 要產生什麼樣子的 AST 是他們自己決定的,不過通常會有個主流的格式,而目前 JavaScript 領域則是 Mozilla SpiderMonkey 的 AST 格式為主,不過其實因為應用太多,也有人在做標準化的工作,叫 estree

April 25, 2015 07:41 AM

April 21, 2015

BobChao

Google Tag Manager 新版學習札記

先前稍微玩了一下 Google Tag Manager 但一直沒有正式放到網站上(主要是... 沒有人有時間再去研究,還有一堆 GA 調整的票掛在那邊沒時間做啊啊啊啊),但因為某顆布丁說要有光就有了光,於是我這星期又重新把沈寂數個月的「研究轉換到 GTM」那張票挖出來。

快速分享一些東西,或許能幫上跟我狀況 / 程度差不多的人。

這篇文:

  • 不是教學,是快速概覽:我是站在「如果是我的話,看了以後可以省一些摸索時間」來寫的,而介面選項在哪裡什麼的,自己摸吧 XD
  • 不是去技術文:GTM 本身雖然沒有限制是開發人員才能使用,但會不會 JavaScript 影響規劃時能用的把戲,我直接以小會一點 js 的角度來看

那就開始囉!

改名

如果你用過舊版的,有些名詞換了,個人認為是更好懂:

  • Rules 改稱為 Trigger
  • Macro 改稱 Variable

改名這種事情,其實對初學者來說超級重要的,因為在求助時,找到的文件可能都還是舊版。

概念

基本流程如下

  1. 新增 Container,就是... 意義上可以歸類的一組東西
  2. 請開發人員將 Container 產生的 GTM 程式碼放上網站。理想上無論你以後怎麼改設定,這就是少數幾次真的會動到網站程式碼的時候了。
  3. 在 Container 中可以新增各種 Tag,可以想為「要做的事」
  4. 每個 Tag 可以藉由多種 Trigger「觸發」
  5. 另外 Trigger 跟 Tag 中可以藉由各種 Variable 來設定雜七雜八的東西
  6. 設定完畢後,可以用 Preview / Debug 來測試
  7. 測試沒問題就發佈,相關設定就會直接套用到網站上
  8. 若以後有要改什麼,就從 Step 3 再走一遍,而開發人員理想上不用更動什麼程式碼

舉例:「使用者在進入這一頁點選『喝我喝我』按鈕後,就告訴 GA 這人在進來多久以後才按這個鈕」,這句話裡:

  • Tag 是「就告訴 GA 這人在進來多久以後才按這個鈕」
    • 其中「進來多久」很可能是 Variable
  • Trigger 是使用者「點選『喝我喝我』按鈕」

Tag 與 Trigger

Tag 的設定不盡相同。簡單的狀況:以設定「讀入此頁時,就在 GA 中記上一筆 pageview」這樣的事情來說,只要新增一個 GA 的 Tag、選擇 Pageview、並且設定在 All page 觸發就結束了。

但複雜的話就很複雜:一樣以 GA 為例,你有「一大堆」的欄位可以指定要傳送怎樣的資訊給 GA,舉凡 UserID、Enhanced eCommerce data 等等都可以(也必須)在這裡設定。這部分有必要搭配相關的開發文件來查閱該怎麼傳。

每個 Tag 都可以指定讓很多種 Trigger 觸發,這裡會是聯集,也就是說如果你設定了 3 個 Trigger,那只要符合 3 個中的任一種情形,都會觸發此 Tag。若想用「交集」,也就是要符合所有條件才會動,那設定上得自己兜成一個 Trigger(單一 Trigger 內的各條件是交集),或者另外新增 Exception。

舉例:我們希望在使用者登入的情況下(這裡有個 Variable 可以得知使用者是否登入),點擊某頁 A 上的某個連結 B 會觸發 Tag。有兩類設定方式:

  1. 在一個 Trigger 裡,設定在 A 頁啟用 Link Click 追蹤,並僅在使用者已登入點擊 B 連結時觸發
  2. 或者,也可以設定某個 Trigger 是在 A 頁啟用 Link Click 追蹤,並僅在點擊 B 連結時觸發。此時該 Tag 還要記得另外新增一個若使用者未登入則不觸發此 Tag 的 Exception

兩種方式主要看規劃,講簡潔好維護當然是 1,但或許某些時候還是會想用 2。

Exception 其實也是一種 Trigger,你可以想成「如果符合這個 Trigger 的條件,我就不觸發」。正所謂敵不動我不動,敵一動我亂動(此句意味不明。)

當然一個 Trigger 也可以觸發兩個以上的 Tag。

Variable

記得先進 Variable 把一些預設的 Built-In Variables 開起來,例如我開了 Page 跟 Click 的大部分。

這些做啥用?例如 Click Element 可以作為 Trigger 的條件,讓你做到「如果點選了 DIV.triggerme 底下的 A 元素,就觸發這個 Tag」,以此類推。

Variable 有許多類型,我覺得比較有用的東西例如:

  • Constant: 常數(固定值),例如 GAID 會用在很多 Tag 上,先拉出來做常數設定好。一個重點是在這邊設定好以後就會變成各欄位的下拉選單。
  • Custom JavaScript: 大概是變化最多最厲害的一種,可以用 JavaScript 抓/拼出各種想要的值交給 GTM。
    • 一行看熱鬧: function(){ return {{Click Element}}.getElementsByClassName("text")[0].getAttribute("data-event-label"); }
    • 其中 {{Click Element}} 是 Variable 的引用方式
    • GTM 高人 Zorro 曾指點,其實 GTM 內建 jQuery,所以不見得要像我那樣用原生 DOM API 抓東西。但... 我就習慣了... 然後沒有公開說支援的東西我也會擔心不告而別壞光光 :/
  • Data Layer Variable: 網頁上送來的 Data Layer 資訊要從這裡轉成變數給其他人用
  • Lookup Table: 如果你會 JavaScript 的話差不多就是 Switch ... Case 的精簡版,需求簡單又懶得寫程式時是蠻方便

Preview / Debug

GTM 內建的除錯工具,啟用後在瀏覽網頁時下半部會分割視窗顯示,平常測試起來會比用瀏覽器內建的 developer console 方便非常多。主要功能是查閱各種狀態(網頁載入、DOM 完備、網頁讀完、各種點擊事件)下:

  • 到底哪些 Tag 被觸發了
    • 以及,送出了什麼資訊
    • 而如果沒被觸發又因為哪些條件沒滿足(這個非常好用)
  • 各自訂變數值

由於他會把各狀態當下的所有數值記錄下來,所以你也可以在十幾個事件之後才回頭查第三次 click 觸發了什麼東西。下圖代表在剛載入的時機點由於網頁 URL 不符規則,於是沒有觸發。

Preview 時的 GTM 設定只有你才看得到,或者你也可以分享給其他特定人。總之,即使在 production 上測試也是沒問題的。

其他筆記

  • 瀏覽器相容性:寫 Custom JavaScript 要注意瀏覽器相容性,畢竟這段 js 碼是真的會在瀏覽器上跑。
  • 由於 GTM 的設計,只要你會寫 JavaScript 就幾乎可以不靠開發人員調整網頁,就能自幹所有的變數(例如,自己爬最終訂單畫面來解析出訂單內容資料)。
    • 不過基礎 Metadata 我會仍然傾向商請網站開發人員送 Data Layer 過來,以免網頁結構有些許更動時讓 GTM 死在路邊。
  • 閱讀文件後我認為 GTM for iOS / Android App 實際作用倒沒有像網站上那麼大,開發人員仍然得自己送出幾乎所有的事件(App 沒有 DOM 可以抓啊...),差異點是:
    1. 有些設定過的 Data Layer 值可以直接重複使用
    2. 可以在軟體發佈以後才透過 GTM 改一些設定值

小結

網站分析師善用 GTM 的話可以省很多「等開發人員有空」的時間,有什麼要調整的東西時開發人員也比較不會覺得很煩。站在「網站企劃分析人員多少都得懂點網頁技術」的角度上,是蠻推薦每個人都可以學學。不過相較 GA 已經深入行銷人的領域、在用詞與操作上幾乎只要懂網路基本原理就可以用,GTM 就還是很技術人的工具。

當然,即使你完全不會寫 JavaScript,也還是可以用 GTM 快樂做些事情、不用等 RD 幫你處理,但會寫 JavaScript 的情況下,用起來根本不是同個級別的。無論如何,Google 提供不少範例,或可作為一個入口。

有打算下個月或下下個月在摩茲工寮弄個簡單的 GTM 分享,有興趣的下面留言一下吧。

by Po-chiang Chao (noreply@blogger.com) at April 21, 2015 02:14 AM

April 07, 2015

Othree

srcset

Responsive Image 大概定案成 srcset<picture> 都有了,src-N 已經消失,雖然我還蠻喜歡,不過總之最近發現 srcset 和我當初介紹時已經差蠻多,中文資源有找到 Zhusee 有另外一篇介紹,不過其實我去看現在的 spec 的時候發現,又有些修改了!最早 srcset 後面是用類似 media query 的設計,後來改成對圖片的 metadata,spec 裡面稱為 descriptor,分別有 width descriptor 用 w 和 density descriptor 用 x,而且限制 srcset 裡面只能用同一種 descriptor,例如全部用 x 或是全部用 w,所以:

  1. 不能在一張圖片裡面同時有 wx
  2. 全部都用 w 或是全部都用 x
  3. 不可有相同的數值,例如兩個 1x 或是兩個 760w
  4. Descriptor 可以算是該圖片的資訊

不過最新的 spec 裡面少了第二點的限制,所以會有一組 srceset 混和 width descriptor 和 density descriptor 的情形,然後怎麼挑選圖片的地方寫說:

In a user agent-specific manner, choose one image source from source set. Let this be selected source.

就是叫瀏覽器自己想辦法的意思,我就很好奇,如果我想設定一組規則,要分成四個組合:

  • 小螢幕低密度
  • 小螢幕高密度
  • 大螢幕低密度
  • 大螢幕高密度

的話,我要怎樣設定 srcset 才能達到目標,因為現在已經不是用 media query 來寫 srcset 了,不能直接寫這樣四組,所以我就去找了 Firefox 和 Chromium 的原始碼來看看他們怎麼做的,Firefox 的找沒多久就找到了,因為他們有 dxr 專案用來方便找程式碼,實際用過覺得真的好用,至於 Chromium 就找比較久了,後來是在 WebKit 裡面有找到,然後發現兩個瀏覽器的原理其實都一樣,要處理同時有 width descriptor 和 density descriptor 的狀況,基本上就是都轉成 density 然後來挑最適合的,作法大致如下,細節可能有誤:

  1. 先對圖片標籤排版,這邊可能會用到 sizes 屬性,不過 CSS 還是優先,然後會得到圖片在頁面上的寬度,稱為 computed width
  2. 對每張候選圖片計算 effective pixel density,算法是: 圖片寬度 / computed width,圖片寬度可以是 width descriptor 來的或是圖片的實際寬度,如果 descriptor 是 density descriptor 的話就不用計算,直接拿來用
  3. 比對 effective pixel density 和現在 device 螢幕的 density,取最接近的

其中第三步驟的比較,大概是考慮效能和記憶體問題,兩個瀏覽器都沒真的做很嚴謹,都是照順序跑過一遍而已,所以在寫 srcset 的時候建議也要照圖片的大小排,至於要大的先還是小的先,就看開發者希望是 density 略大的優先還是略小的優先了,所以如果我寫:

srcset="aaa.jpg 1x, bbb.jpg 1.4x, ccc.jpg 1.6x, ddd.jpg 2x"

然後我現在圖片需要 1.5x 的話,應該就會拿到 1.4x 的 bbb.jpg,而如果我偏好用 1.6x ccc.jpg 的話,就要改成:

srcset="ddd.jpg 2x, ccc.jpg 1.6x, bbb.jpg 1.4x, aaa.jpg 1x"

後來發現這個挑選圖片的原則,其實在 WHATWG 的 HTML Spec 裡面有寫,不過是 non-normative 的段落,就是說這不是一定要遵守,只是建議,而且前面也有提到 spec 內是寫說挑選的原則是瀏覽器自己處理,而會這樣設計相信是為了像 mobile device 之類的裝置,網路速度如果比較慢,或是需要節省流量時,就可以挑選比較小的檔案,而不一定是挑出顯示上最好的那張圖片。

最後,其實 descriptor 除了 density 和 width 兩種之外,還有一種 height descriptor,不過目前只是保留可能性,spec 還沒定義要怎樣處理,其實還蠻能理解目前會以寬度為主的狀況,在 Matt Brubeck 的 Let's build a browser engine! 系列文章中的第六篇「Block layout」這篇文章有介紹到一般瀏覽器是怎樣排版畫 layout 的,而其處理的原則就是先從左上角開始把東西往右排,所以寬度一定先決定,然後才決定高度,相信這也是垂直置中搞這麼多年的原因吧。

April 07, 2015 04:53 PM

April 05, 2015

Othree

Loader

Loader 是 ECMAScript 定義要來處理 module import/export 等等事情的底層介面,ES6 的 module 我一直都很好奇,到底要怎麼去找 import 進來模組的原始碼,會好奇這點是因為如果是網頁環境,所有其它模組的原始碼一定是在遠端的 server 上,要拿到勢必是一個 request,然後還要等下載,總之就是非同步的流程,以前在 ES5 的話,要做非同步控制大概就是要做成 callback 的形式,所以會有像 AMD 那樣的設計出來,加上 module 名稱和檔案名稱可能又有差別,像是差個副檔名之類的,而 ES6 提供了原生的 import/export 語法來定義 module,所以我就很好奇它的底層要怎樣設計才能標準化。

Addy Osmani 有建立一個 Loader 的 polyfill 給 ES5 環境使用 Loader API,就叫做 ES6 Module Loader Polyfill,這個 polyfill 內部實做是照當初 ES6 draft 來寫的,其實還蠻複雜,不過把讀取一個 module 的事情拆分一下,可以分成以下幾個步驟:

  1. Normalize:根據給的名稱取得實際的 module name
  2. Locate:根據實際的 module name 取得 module 的位置
  3. Fetch:根據 module 位置去取得檔案內容
  4. Translate:如果有需要對檔案內容作任何修改,就在這裡處理
  5. Instaniate:最後是根據檔案內容(程式碼),判斷有哪些相依模組和知道如何初始化這個模組

以上幾個步驟是屬於 Loader 可自訂化的部分,到 instaniate 完成後,Loader 就繼續去讀其它相依的模組,相依模組都準備好之後,就可以使用模組的的程式碼,正式的把模組建立起來,ES spec 細部還定義了很多實做細節,像是非同步的操作都是用 Promise 來做流程控制,還有 Loader 也有個 module 的 registry 可以來保存已經讀好的模組,就不用一直重新建立,另外還有一些內部溝通的資料結構,像 instaniate 步驟要回傳一個物件,裡面有兩個屬性分別是 depsexecutedeps 是相依模組名稱的陣列, execute 則是該模組本身的初始化函數,參數的數量要剛好和相依模組數量一樣,回傳的則是 Module 物件等等。

其實目前的 ES6 spec draft 已經把 Loader 拿掉了,TC39 決定把他獨立出來,目前是 WHATWG 再接手繼續,不過目前的最新版本已經改很大了,看起來感覺有簡化不少,不過上面的五個步驟基本上還是存在,我一開始看到這五個步驟加上說可以自訂還沒什麼感覺,只是覺得奇怪為什麼細節沒寫,沒錯,這五個步驟在 spec draft 都只有介紹目的,不像其它的操作有詳細的寫出 method 內的流程,關鍵的地方就在於 JavaScript 已經不是單純只是在瀏覽器上跑的語言了,把這部分抽象化就是為了讓它可以同時在瀏覽器環境和單機環境下都可以實做,根據不同的 JS 環境去實做相對應的步驟細節,像是 fetch 在瀏覽器下就是真的用 fetch 去拉檔案,但是在 node 下就變成讀檔案,而在 ES6 Module Loader Polyfill 下,就有實做一組瀏覽器環境下的操作,不過這組操作的實做未來也不會真的進到瀏覽器內,最大的問題在於第五個步驟的 instaniate。

Instaniate 這個步驟是要實做 ES6 module 一個很關鍵的部分,關鍵之處在於要把 module 的 dependency 找出來,在 ES6 module 裡面,有一個限制是每個 module 都必須要獨立一個檔案,所以不能一個檔案定義兩個 module,然後假設瀏覽器已經支援 ES6 module 了,只要 parse 程式碼成 AST 找出裡面的 import 就可以把 dependency 列出來。不過現在是 polyfill,瀏覽器也還不支援 import,所以要實做 instaniate 自然需要能處理這個問題,ES6 Module Loader Polyfill 的作法是使用 transpiler,目前支援 TraceurBabel,把本來用 importexport 寫的模組轉成用類似的 AMD 模組定義的型式,而這邊用的型式是 systemjs 裡面提供的 System.register,這個方法本身並不是 ES spec 裡面定義的,比較像是為了處理這尷尬時間點所設計的替代方案。

本來我是想看看,是不是能夠只靠 Loader 就把 ES6 的 module 機制在現在的瀏覽器上建立起來,結果發現只靠 ES6 Module Loader Polyfill 是辦不到的,Loader API 並沒有定義模組的語法,如果用 ES6 的語法來定義模組還需要 transpiler 來從程式碼中分析出 module dependency,不過我不太想要把整包 transpiler 也放到 translate 裡面用,雖然可以自己寫一個什麼事情都不做的 translate function,但是要解決 dependency 的話還是會需要像 System.register 的幫助,總之到這邊,可以發現一個重點是,Loader 不管 module 定義的方法,雖然假想情境下是用 ES6 module 語法,每個檔案代表一個 module,然後用 importexport 來定義相依模組和提供的 method,不過其實 Loader 也是可以處理 AMD、CommonJS 甚至是 NodeJS 型式的模組定義,只是要有人去實做這部分的 translate 和 instantiate 的部分,而 SystemJS最新版(0.16)就是這樣一個專案,它號稱是 universal module loader,支援 AMD、CommonJS、NodeJS 和 ES6 的模組定義,然後在最新的版本,改成使用 ES6 Module Loader Polyfill 的機制來做 module 的讀取、相依性的判斷和模組初始化,雖然有些地方沒有真的照之前的 spec 來實做。

另外一個之前不太清楚的問題也藉此搞清楚了,ES6 module 有限制一個檔案定義一個 module,而現在的 web application 常常為了效能問題,都把多個檔案合併成一個檔案,這時就不能用 ES6 module 了,當然也可以用像現在 SystemJS 的作法來處理,不過其實再過一兩年 HTTP/2 普及後,也不需要這樣搞了,會變成只需要 minimize,這部分倒是還可以接受。

ES6 裡面的 Loader 本身其實是一個 constructor,放在另外一個 ES6 提供的新東西 Reflect 下面,而用 Reflect.Loader 建立 Loader instance 時可以順便給他一些參數,像是前面提到的讀模組的五個步驟的實做,或是領域(realm),而 System 物件則是該 JS 環境下的預設的 Loader,理論上如果是瀏覽器環境,它就會知道怎樣去 fetch 遠端的程式碼回來,如果是 NodeJS 就會改用 file system 讀檔案,而且也知道要把模組放到那個 realm(理論上不同 frame 就是不同 realm),這樣大部分的使用都可以用 System 就好了,只有很少數的情形需要自己建立 Loader。其實上面說的 Loader API 不知道為什麼是移到 WHATWG 之後,幾乎是重新開始編寫,完整度欠佳,有些章節還是空的,另外也沒有定義 System 或是其它的新的替代方案,所以現在想要看看到底 Loader 內部怎麼做的話,要看舊版的 spec,可以去抓 2014 年 8 月的 ES6 draft rev 27 然後看看 CH 26, 15,對照 ES6 Module Loader Polyfill 的程式碼可能比好懂。

April 05, 2015 06:50 AM

March 30, 2015

timdream

Service Worker and the grand re-architecture proposal of Firefox OS Gaia

TL;DR: Service Worker, a new Web API, can be used as a mean to re-engineering client-side web applications, and a departure from the single-page web application paradigm. Detail of realizing that is being experimented on Gaia and proposed. In Gaia, particularly, “hosted packaged app” is served as a new iteration of security model work to make sure Service Workers work with Gaia.

Last week, I spent an entire week, in face-to-face meetings, going through the technical plans of re-architecture Gaia apps, the web applications that powers the front-end of Firefox OS, and the management plan on resourcing and deployment. Given the there were only a few of developers in the meeting and the public promise of “the new architecture”, I think it’s make sense to do a recap on what’s being proposed and what are the challenges already foreseen.

Using Service Worker

Before dive into the re-architecture plan, we need to explain what Service Worker is. From a boarder perspective, Service Worker can be understood as simply a browser feature/Web API that allow web developers to insert a JavaScript-implemented proxy between the server content and the actual page shown. It is the latest piece of sexy Web technologies that is heavily marketed by Google. The platform engineering team of Mozilla is devoting to ship it as well.

Many things previously not possible can be done with the worker proxy. For starter, it could replace AppCache while keeping the flexibility of managing cache in the hand of the app. The “flexibility” bits is the part where it gets interesting — theologically everything not touching the DOM can be moved into the worker — effectively re-creating the server-client architecture without a real remote HTTP server.

The Gaia Re-architecture Plan

Indeed, that’s what the proponent of the re-architecture is aiming for — my colleagues, mostly whom based in Paris, proposed such architecture as the 2015 iteration/departure of “traditional” single-page web application. What’s more, the intention is to create a framework where the backend, or “server” part of the code, to be individually contained in their own worker threads, with strong interface definitions to achieve maximum reusability of these components — much like Web APIs themselves, if I understand it correctly.

It does not, however, tie to a specific front-end framework. User of the proposed framework should be free to use any of the strategy she/he feel comfortable with — the UI can be as hardcore as entirely rendered in WebGL, or simply plain HTML/CSS/jQuery.

The plan is made public on a Wiki page, where I expect there will be changes as progress being made. This post intentionally does not cover many of the features the architecture promise to unlock, in favor of fresh contents (as opposed of copy-edit) so I recommend readers to check out the page.

Technical Challenges around using Service Workers

There are two major technical challenges: one is the possible performance (memory and cold-launch time) impact to fit this multi-thread framework and it’s binding middleware in to a phone, the other is the security model changes needed to make the framework usable in Gaia.

To speak about the backend, “server” side, the one key difference between real remote servers and workers is one lives in data centers with endless power supply, and the other depend on your phone battery. Remote servers can push constructed HTML as soon as possible, but for an local web app backed by workers, it might need to wait for the worker to spin up. For that the architecture might be depend on yet another out-of-spec feature of Service Worker, a cache that the worker thread have control of. The browser should render these pre-constructed HTML without waiting for the worker to launch.

Without considering the cache feature, and considering the memory usage, we kind of get to a point where we can only say for sure on performance, once there is a implementation to measure. The other solution the architecture proposed to workaround that on low-end phones would be to “merge back” the back-end code into one single thread, although I personally suspect the risk of timing issues, as essentially doing so would require the implementation to simulate multi-threading in one single thread. We would just have to wait for the real implementation.

The security model part is really tricky. Gaia currently exists as packaged zips shipped in the phone and updates with OTA images, pinning the Gecko version it ships along with. Packaging is one piece of sad workaround since Firefox OS v1.0 — the primary reasons of doing so are (1) we want to make sure proprietary APIs does not pollute the general Web and (2) we want a trusted 3rd-party (Mozilla) to involve in security decisions for users by check and sign contents.

The current Gecko implementation of Service Worker does not work with the classic packaged apps which serve from an app: URL. Incidentally, the app: URL something we feel not webby enough so we try to get rid off. The proposal of the week is called “hosted packaged apps”, which serve packages from the real, remote Web and allow references of content in the package directly with a special path syntax. We can’t get rid of packages yet because of the reasons stated above, but serving content from HTTP should allow us to use Service Worker from the trusted contents, i.e. Gaia.

One thing to note about this mix is that a signed package means it is offline by default by it’s own right, and it’s updates must be signed as well. The Service Worker spec will be violated a bit in order to make them work well — it’s a detail currently being work out.

Technical Challenges on the proposed implementation

As already mentioned on the paragraph on Service Worker challenges, one worker might introduce performance issue, let along many workers. With each worker threads, it would imply memory usage as well. For that the proposal is for the framework to control the start up and shut down threads (i.e. part of the app) as necessary. But again, we will have to wait for the implementation and evaluate it.

The proposed framework asks for restriction of Web API access to “back-end” only, to decouple UI with the front-end as far as possible. However, having little Web APIs available in the worker threads will be a problem. The framework proposes to workaround it with a message routing bus and send the calls back to the UI thread, and asking Gecko to implement APIs to workers from time to time.

As an abstraction to platform worker threads and attempt to abstract platform/component changes, the architecture deserves special attention on classic abstraction problems: abstraction eventually leaks, and abstraction always comes with overhead, whether is runtime performance overhead, or human costs on learning the abstraction or debugging. I am not the expert; Joel is.

Technical Challenges on enabling Gaia

Arguably, Gaia is one of the topmost complex web projects in the industry. We inherit a strong Mozilla tradition on continuous integration. The architecture proposal call for a strong separation between front-end application codebase and the back-end application codebase — includes separate integration between two when build for different form factors. The integration plan, itself, is something worthy to rethink along to meet such requirement.

With hosted packaged apps, the architecture proposal unlocks the possibility to deploy Gaia from the Web, instead of always ships with the OTA image. How to match Gaia/Gecko version all the way to every Nightly builds is something to figure out too.

Conclusion

Given everything is in flux and the immense amount of work (as outlined above), it’s hard to achieve any of the end goals without prioritizing the internals and land/deploy them separately. From last week, it’s already concluded parts of security model changes will be blocking Service Worker usage in signed package — we’ll would need to identify the part and resolve it first. It’s also important to make sure the implementation does not suffer any performance issue before deploy the code and start the major work of revamping every app. We should be able to figure out a scaled-back version of the work and realizing that first.

If we could plan and manage the work properly, I remain optimistic on the technical outcome of the architecture proposal. I trust my colleagues, particularly whom make the architecture proposal, to make reasonable technical judgements. It’s been years since the introduction of single-page web application — it’s indeed worthy to rethink what’s possible if we depart from it.

The key here is trying not to do all the things at once, strength what working and amend what’s not, along the process of making the proposal into a usable implementation.

Edit: This post have since been modified to fix some of the grammar errors.

by timdream at March 30, 2015 10:39 PM