Blackbing Playground

[ReactJS] jsx or coffee style

JSX

在寫 ReactJS 時,官方建議使用 jsx 這樣的語法來處理 html,這有點像是 template engine,不知道什麼是 jsx 的同學可以在 jsx compiler 玩玩看。jsx 其實不是必要的東西,如果你開心的話也可以直接用

jsx 充滿了疑惑,要做一些邏輯性的操作(例如 if-else, foo-loop)等等,相對的有點麻煩。
1
2
3
4
5
6
7
8
9
10
11
12
13
後來看到了一篇文章,推薦用 coffeescript 的特性來直接撰寫 React component:[React, JSX, and CoffeeScript](http://neugierig.org/software/blog/2014/02/react-jsx-coffeescript.html)。文章提到用 coffeescript 的特性來操作 component 會更加的得心應手,於是我也動手把 component 直接改成 coffee style。寫出來的程式大概長成這樣:
```coffeescript
R.div id: 'list-view', className: 'view tab-pane fade in active',
if listType is 'foo'
R.div className: 'back', '←'
R.p className: 'alert alert-warning',
R.i className: 'fa fa-exclamation-circle'
if listType is 'bar'
R.span null, 'Hello world'
else
R.span null, 'Magic!'

優點

利用縮排來 beautify 程式

1
2
3
4
R = React.DOM
R.p null,
R.a href:'foo', 'bar' # note omitted comma here
R.a href:'foo2', rel:'nofollow', 'second link'

利用 foo-loop 來產生 template

1
2
3
R.ol null,
for result in @results
R.li key:result.id, result.text

相比之下用 jsx 就囉唆許多:http://facebook.github.io/react/docs/multiple-components.html

寫 if-else 像是在喝水的簡單

1
2
3
4
5
6
7
8
9
10
11
if not @state.editing
R.div null,
@props.text
' '
R.span className:'link-button mini-button', onClick:@edit, 'change'
else
R.div style:{position:'relative'},
R.input
style:{position:'absolute', top:-16, left:-7}
type:'text', ref:'text', defaultValue:@props.text
onKeyUp:@onKey, onBlur:@finishEdit

相比之下 jsx 的 if-else 限制很多:http://facebook.github.io/react/tips/if-else-in-JSX.html

缺點

但是一直到最近我放棄了,所以我來說說 coffee style 的缺點。

無法使用在太複雜的 DOM 結構

其實可想而知,若DOM結構複雜的話,這樣做會搞死自己,雖然我們自以為對 javascript/coffee 非常熟稔,但人眼終究敵不過 template 的複雜度。這是一段我曾經寫的 component 的程式,坦白說這個結構沒有很複雜,但我看第二次的時候我自己都想殺了我自己,大家可以笑笑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
R.fieldset(className:"form-group #{ex_className}",
R.legend( {className:"scheduler-border"}, item.name),
R.div( {className:"table-responsive"},
R.table( {className:"table table-hover"},
R.tbody(null,
for d_items, key in item.dynamic_item
console.log d_items
R.tr(null,
for i of d_items
d_item = d_items[i]
if d_item.type is 'dropdown'
R.td(null, DropdownItem(item:d_item))
else
R.td(null, TextItem( item: d_item))
R.td(null,
R.button( {type:"button",className:"btn btn-default btn-sm",
onClick: do(key)=>
=> @removeItem(key)
},
R.i( {className:"glyphicon glyphicon-remove"})
)
)
)
)
),
R.div(null,
R.button( {
type:"button"
className:"btn btn-info btn-xs extend form-add"
onClick: @addItem
},
R.i( {className:"glyphicon glyphicon-plus"})
)
)
)
)

難以維護

由於 coffee 終究是 javascript,用 coffee 來寫基本上就只是在寫程式,一開始我對於可以使用 underscore 之類的 library 來寫 react component 感到很興奮,最後發現這根本沒辦法維護。比方說,我們可以這樣用

1
React.DOM.div null, foo.bar.map( (r)-> fruit[r]).join(', ')

看起來很簡單,而且寫起來很乾淨,但是當 template 摻雜著太多的邏輯,可就不好玩了,因為你很難控制團隊中大家會怎麼去使用這些東西,其實 template 應該還是回歸單純,只能接收 data,顯示 data。邏輯等等的東西應該都不要跟 template 有太多瓜葛。

結論

在經過一段時間的實做之後,我認為這些缺點非常致命,因此我不建議使用 coffee 來寫 react component,雖然說官方也提到 jsx 不是必要,但若是團隊合作的專案之中,建議還是乖乖的使用 jsx 來處理 template,盡量將邏輯與 template 分開。

下次會分享一些使用 jsx 的小技巧。