這個週末因緣際會之下看到了這個 - Web Spreadsheet in 99 Lines。進去逛逛之後發現作者的 code 竟然是使用 ES6 JavaScript 來寫的,當然同時也有 compile 出 ES5 的版本供瀏覽器跑。
加上日前觀看某 React.js 的影片裡面小小的秀了一下 CoffeeScript,喚醒了我內心對偷懶(?)的渴望,開始到處查找目前上手 ES6 開發的文章,最後輾轉回到了 CoffeeScript (WTF)。
怎麼會回到 CoffeeScript!? 當下的幾個考量如下:
- ES6 其實感覺相對還是少人用,而且真的要走入各瀏覽器還需要一些時間
- 先用 CoffeeScript,到時候還是可以 compile to ES6
- 其實我只是想偷懶而已,不是真的想試 ES6
那為什麼不是更先進一點的 LiveScript 呢?
- 生理上有點不能接受 LiveScript
- 用的人比 CoffeeScript 少
說到生理上不能接受 LiveScript,其實對 CoffeeScript 我也是抱持一樣的想法。雖然 CoffeeScript 一副就是覺得用自己寫起來程式碼會簡單清楚又好讀的樣子,可能由於我本身是一個比較 old-fashioned 的人,我覺得完全不是這麼一回事… 還是傳統的 JavaScript 比較一目瞭然。
當然現在為了偷懶就必須要有所妥協。相較於 LiveScript,CoffeeScript 還是比較和藹可親一點(果然是一個越先進越不和藹的概念),可能以後有機會才會比較能接受 LiveScript 吧!
於是我週末花了一些時間把目前手上某個專案的部分 code 轉成 .coffee,並且把 build flow 用 grunt 建立起來。
是說練習寫了 1000 行以上的 CoffeeScript 果然就比較適應了。
本文主要是想整理上述過程中獲得的一些情報,大致上可以分為三個部分:
- Grunt tasks
- Sublime Text 3 packages
- My style guide
Grunt tasks
目前來講如果某部分的 code 已經整塊全轉成 .coffee ,我會將它們 compile 到暫存資料夾再來做 uglify。
而對需要混合寫的 code,目前是採用 compile 到對應資料夾裡跟一般的 .js 擺一起,不過附檔名用 .coffee.js 來做區隔。這樣做的好處是:
- 可以完全融入原本純 JavaScript 環境的 build flow
- 可以用 .gitignore 避掉 .coffee.js 檔案們
grunt-contrib-coffee
用來 compile CoffeeScript 成 JavaScript。
grunt-coffeelint
透過 CoffeeLint 做 CoffeeScript 語法檢查。
Sublime Text 3 packages
想要快樂的寫 CoffeeScript,當然要把刀子磨亮啦!
Better CoffeeScript
除了提供程式碼上色以及一些小 snippet 之外還有一些運用 CoffeeScript 的功能 這邊列出預設快捷鍵來 preview 一下這個 package 所提供的功能
alt+shift+t - Run a Cake task
alt+shift+r - Run some CoffeeScript (prints output to a panel on the bottom)
alt+shift+s - Run a syntax check
alt+shift+c - Compile a file
alt+shift+d - Display compiled JavaScript
alt+shift+l - Display lexer tokens
alt+shift+n - Display parser nodes
alt+shift+w - Toggle watch mode
alt+shift+p - Toggle output panel
CoffeeCompile
可以直接在程式碼選取一個片段直接 compile 成 JavaScript,並且在類似 console panel 的地方顯示出結果。
個人覺得使用 CoffeeScript 的初期還蠻適合用來做一些小實驗。
SublimeLinter-coffeelint
SublimeLinter3 搭配 CoffeeLint 的 package。
My style guide
這邊列出一些經過實驗之後我決定的寫法,基本上看個人偏號使用囉!
Invoke function 的時候盡可能不要帶括號
這項可能待驗證,生理上我比較能接受帶括號的寫法,還是比較清楚。
各種 invoke 法:
# 無參數
it = -> console.log 'No just?'
do it
# 最一般的 (with object literal)
chrome.alarms.create THROTTLE_ALARM_NAME, periodInMinutes : 1
# 有一個 callback
chrome.alarms.onAlarm.addListener (alarm) ->
throttled = false if alarm.name is THROTTLE_ALARM_NAME
# 有兩個 callback Part1
# 此時為了表達晰以及寫法整齊可以不用 anonymous function
success = (data, status) ->
"success"
error = (data, status) ->
"error"
xhrGet "some url", success, error
# 有兩個 callback Part2
# 如果硬要寫 anonymous function 就會變這成這樣
xhrGet "some url",
(data, status) ->
"success"
, # 會覺得這個逗號很有事
(data, status) ->
"error"
盡量不要使用內建的 return 最後一行功能來 return
想 return 什麼就寫出來。
genEvtHandler = (key) ->
return (evt) ->
console.log "event handler"
判斷式內的 function call 寫法
getRealNumber = (number) ->
return number * 10
# 地雷式寫法
if getRealNumber 5 >= 10
# compiles to if (getRealNumber(5 >= 10))
console.log '這是地雷'
# 改良式寫法
if getRealNumber(5) >= 10
# compiles to if (getRealNumber(5) >= 10)
console.log '好像不錯'
# 看起來更 coffee 一點
if (getRealNumber 5) >= 10
# compiles to if ((getRealNumber(5)) >= 10)
console.log '就決定是你了'
如果判斷式內容過長或著內容需要寫多行,避免使用後置 if
好的 case:
if 200 <= request.status < 400
success data, request.status if success
else
error data, request.status if error
不好的 case 及修正:
# Bad
chrome.tabs.sendMessage(tab_id,
message : mapped_message
key : key
charIndex : evt.charIndex) if mapped_message
# Good
if mapped_message
chrome.tabs.sendMessage tab_id,
message : mapped_message
key : key
charIndex : evt.charIndex
使用 and
, or
, is
, isnt
取代 &&
, ||
, ==/===
, !=/!==
使用 and
跟 or
讓 expression 看起來語感好一些。
另外由於在 CoffeeScript 中,==
會直接 compile 成 ===
且 !=
會直接 compile 成 !==
,讓我覺得豈不是直接用 is
跟 isnt
就好了嗎…
老實說有時候還是想依靠一下 JavaScript 那混亂的 ==
跟 !=
就是了。
使用 object literal 的時候給它一點空間
其實是因為在 CoffeeLint 的 options 裡有看到一項 colon_assignment_spacing
,考慮遵守一下。
chrome.tabs.sendMessage tab_id,
message : mapped_message
key : key
charIndex : evt.charIndex
平常字串都使用單引號,除非有變數內插的需求
# Normal
a = '平常字串'
# Interpolation need
foo = 'Coffee'
b = "#{foo}Script"
使用 global 內容的 pattern
這邊以 angular
為例。
#global angular
root = exports ? this
angular = root.angular
支援 ngmin 的 angular DI 寫法
為了整合進原本的 JavaScript flow,並且不想手動作 dependency annotation,要生出 ngmin 認得的格式才行。
這邊用 factory
做範例。
(angular.module "myApp").factory 'thisIsAService',
($rootScope, $http, $cacheFactory) ->
# Explicit return
return ->
# Factory function
後記
是說用本文標題去 Google 真的可以搜到一堆 CoffeeScript 相關的文章、影片、投影片之類的…
看來爛梗大家都… (誤)