
CoffeeScript是基於JavaScript的一門擴展小巧語言,它需要編譯成JavaScript,然后再運行與瀏覽器或者Nodejs平台。JavaScript由於商業原因10天時間就匆忙誕生,所以存在很多弊病。但如《JavaScript精粹》一書中所說:JavaScript也存在着一顆華麗的心臟,如果我們能避開JavaScript中的“坑”,使用其精華的部分,這將是一門令人愛不釋手的語言. 而CoffeeScript則是嘗試使用這部分簡潔的方式展示JavaScript的這部分優秀的精華,避免那些困擾JavaScript開發者的“坑”.CoffeeScript借鑒於Python和Ruby這兩門語言,函數式風格、鴨子類型、OO風格一體的一門簡潔語言。
Angularjs從2012年以來是火極一時的前端MVVM框架,它引入了module、雙向綁定、依賴注入、Directive、MVVM等機制。更多資料參見博主其他博文。當Angular遇見CoffeeScript這門語言,將會發生什么呢?
想讓我們來看一眼筆者利用CoffeeScript對Angular1.x代碼的封裝后效果。
## controller
class DemoController extends NgComponent
@inject 'demoService'
@controller 'ng.green.demo'
__init__: =>
@demoService.getLang().then (data) =>
@lang = data
## service
class DemoService extends NgComponent
@inject '$q'
@service 'ng.green.demo'
getLang: =>
data = data : ['JavaScript', 'CoffeeScript', 'TypeScript', 'ES6']
@$q.when(data)
## directive controller
class JsonDumpController extends NgComponent
@inject '$log'
@controller 'ng.green.demo'
__init__: =>
@$log.info('This is form directive controller')
## directive
class JsonDumpDirective extends NgComponent
@inject '$timeout', '$http', '$cacheFactory', '$log'
@directive 'ng.green.demo'
restrict: 'EA'
templateUrl: '/jsonDump.html'
scope:
json: "="
controller: 'JsonDumpController'
link: (scope, elm, iAttrs) =>
@$timeout (() => @$log.info '$timeout & $log injector worked on link function!' ), 100
有了上面的對controller、service、directive的定義,則我們可以如下方式使用:
<div ng-app="ng.green.demo" ng-controller="DemoController as demo" class="container">
<json-dump json="demo.lang"></json-dump>
<script type="text/ng-template" id="/jsonDump.html">
<hr />
<pre></pre>
</script>
</div>
不知各位看官對如上代碼感覺如何?是不是更簡化、語義化、有點ng的感覺。其中筆者還有意模仿Python,如init作為初始化方式。在這里每個class會自聲明組件類型,以及聲明式注入,module自注冊。
不管如何看,下面我來看看NgComponent到底做了什么?
class NgComponent
@controller: (moduleName, moduleResolver) ->
componentName = @$$componentName(true)
angular.module(moduleName, moduleResolver).controller componentName, @
@service: (moduleName, moduleResolver) ->
componentName = @$$componentName()
angular.module(moduleName, moduleResolver).service componentName, @
@directive: (moduleName, moduleResolver) ->
componentName = @$$componentName().replace('Directive','')
directiveClass = @
directiveFactory = (args...) ->
new directiveClass(args...)
directiveFactory.$inject = @$inject
angular.module(moduleName, moduleResolver).directive componentName, directiveFactory
@$$componentName: (upperCaseFirstLetter = false) ->
# regex for ie
componentName = @name || @toString().match(/function\s*(.*?)\(/)?[1]
if upperCaseFirstLetter
firstLetter = componentName.substr(0,1).toUpperCase()
else
firstLetter = componentName.substr(0,1).toLowerCase()
(componentName = "#{firstLetter}#{componentName.substr(1)}") unless upperCaseFirstLetter
componentName
@inject: (args...) ->
@$inject = args
constructor: (args...) ->
for key, index in @constructor.$inject
@[key] = args[index]
@__init__?()
在NgComponent中定義了controller、service、directive注冊接口,這里可以是聲明創建module,也可以是在已聲明的module上注冊這些組件類型。對於組件命名也才采用了約定勝於配置,它們都以class類型為基礎,controller為首字母大寫的駝峰命名,service則首字母小寫駝峰命名,directive則會去掉Directive標記並首字母小寫注冊。
同時這里也聲明了@inject方法,使得我們可以在定義在類型之上定義$inejct屬性,Angular的注入聲明。對於Angular的注入服務,在構造函數中會將他們一一添加到當前類實例對象之上。在依賴添加完畢后,也會調用對象初始化方法,這里是模擬Python的init。
Demo效果可以在codepen查看 http://codepen.io/greengerong/pen/EVVQZg?editors=101:
See the Pen Angular meet CoffeeScript by green (@greengerong) on CodePen.
本文筆者的突發奇想,希望能給讀者一些啟發,也許你還有更好的DSL封裝,歡迎多多交流。
