React Cjsx 还是挺好使的...

luikore · 2015年11月07日 · 最后由 zzxdsra 回复于 2016年03月08日 · 12009 次阅读
本帖已被管理员设置为精华贴

webpack.config.js 加入 cjsx-loader

var production = (process.env.RACK_ENV == 'production')

module.exports = {
  ...
  plugins: (production ? [
    new webpack.DefinePlugin({
      "process.env": { NODE_ENV: '"production"' } // reduce react size
    }),
    new webpack.optimize.UglifyJsPlugin(),
  ] : [
    new webpack.HotModuleReplacementPlugin()
  ]),
  module: {
    loaders: [
      ...
      { test: /\.cjsx$/,   loader: (production ? 'coffee!cjsx' : 'react-hot!coffee!cjsx') },

匿名 class

React = require 'react'
module.exports = class extends React.Component
  render: ->
    <div></div>

Comprehension 渲染数组,比 Array.map 更有效率,比 for 循环 push 更简洁

<div>
  { <div key={item.id} onClick={ @props.onDelete.bind null, i }>
      Delete {item.name}
    </div> for item, i in items }
</div>

利用 pattern matching + for ... by 实现 ruby 中的 each_slice 或者 lodash 的 chunk 的效果

<div>
  { <tr key={a.id + b.id}>
      <td>{ a }</td>
      <td>{ b }</td>
    </tr> for [a, b] in rows by 2 }
</div>

如果 child component 要和 parent 共享 state, 那么应该由 parent 控制 state, 而 child 只需要接受 props, 控制 state 的方法可以由 props 传入

假设有 Page, 有内部状态 items

class Page extends React.Component
  getInitialState: ->
    items: [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}]

  deleteItem: (i) ->
    @state.items[i..i] = []
    @setState @state

  render: ->
    <div>
      <h1><span>{@state.items.length}</span> items</h1>
      <Items key={item.id} items={@state.items} onDelete={@deleteItem} />
    </div>

用 Coffee 可以这样实现 Items (func.bind(null, ...) 不改变 this ):

class Items extends React.Component
  render: ->
    <div>
      { <div key={item.key}>
          <a onClick={@props.onDelete.bind null, i}>{item.name}</a>
        </div> for item, i in @props.items
      }
    </div>

或者用 Array.map

class Items extends React.Component
  render: ->
    <div>
      { @props.items.map (item, i) =>
          <div key={item.key}>
            <a onClick={=> @props.onDelete i}>{item.name}</a>
          </div>
      }
    </div>

或者用 do 语法...

class Items extends React.Component
  render: ->
    <div>
      { <div key={item.key}>
          <a onClick={do (i) => => @props.onDelete i}>{item.name}</a>
        </div> for item, i in @props.items
      }
    </div>

es6 不是挺好的么?

我以前也挺喜欢 CoffeeScript,甚至把一个写了一半的 Ember app 从 JavaScript 全部转成 CoffeeScript 了。

不过自从用了“ES2015 + 不打分号的编码风格 + 数组末尾也可以打逗号”后,我就不怎么想念 CoffeeScript 了。CoffeeScript 在我看来有几点不如 ES2015,并且以后也没法改进:

函数必定有返回值

这在写 event handler 和 Promise 时影响非常大。前者返回 true 有可能导致意外地事件冒泡,后者就不说了。有时候我必须提醒自己一定要写 return 。ES2015 的 array function 在 () =>xxx 语法下才有自动返回值,() => { xxx } 情况下不会有,这点控制得更好。

某些 ES2015 语法没法用

比如 import/export ,必须用 Embedded JavaScript 语法去写,这会导致一些问题,比如 export default class X 这种语法在 CoffeeScript 必须拆成两行。我想 async/await 语法估计也如此。

可选择的编译规则

ES2015 可以根据平台支持程度选择性地 transpile 特性,可能未来就完全不需要 transpile 了。举个例子,如果用 Cordova 开发 Android 应用并选择了 Crosswalk 作为 webview,我完全知道平台支持什么语言特性,以此针对性地关闭 Babel.js 的某些 transpiler。而 CoffeeScript 必须一直编译。老实说拿这个来比较不公平,观点就见仁见智了。

CoffeeScript 是个不错的语言,不过我觉得它已经完成历史使命了。

#2 楼 @darkbaby123

babel 是不错,但 coffee 还有几点特别有用的特性,现在还没有替代的:

  • 少写很多括号和 this
  • do (n) => ... 相当于 ((n) => ...)(n)
  • 高效又简洁的 for ... by ... when ...
  • Comprehension 是 ES7 特性,babel 不支持
  • Range 和 Array splicing. 例如删除下标为 5 的元素怎么写?Coffee 里只需要 a[5..5] = [], 而 ES 的 splice 函数非常费解
  • switch .. when 不用手动 break
  • /// 可以围起 block regexp 而 ES 依然要一行行加起来
  • yield 就自动变成 function*

一些语法区别例如 in, of 是个问题,但其实是 js 的 in 设计错了,坑了 coffeescript ...

https://github.com/hemanth/coffeescript-equivalents-in-es6

自打发现了 cjsx,我就再也没写过 jsx 和 es6……

#3 楼 @luikore

不过 coffee 的源码更新已经很少了,babel 现在还是很活跃的,es7 也是指日可待。

@luikore 因为 ES“只增不改”的策略,有些东西一辈子都会改动了。像 switch .. whensplicefor .. in 都属于这种。有些以后还有少许机会推出新的语法和 API 改进,然后把老语法列入不推荐的行列。但如果对现有语法有不同看法并且不喜欢这种设计,那也确实只能一直用 compile to JavaScript 的语言了。

8 楼 已删除

各位大牛有没有关于 cjsx 的介绍的文档啊,这是个什么文件?

需要 登录 后方可回复, 如果你还没有账号请 注册新账号