CJSX 试用 – 使用 React 靠谱的新姿势

背景

React 真是个好东西,不过 JS 语法有时候还是难免显得臃肿。ES6 虽然改进了许多,不过还是有很多槽点。比起来我感觉 CoffeeScript 就舒服了许多。

React 魔改了一番 JS,使其能在 JS 代码中直接插入 HTML 代码,这样魔改之后的 JS 叫做 JSX。本文对 JSX 就不做过多的赘述了,我们直接来说 CoffeeScript 版本的 “JSX”——CJSX。虽然说是新姿势,不过其实 cjsx 已经有一段时间了。不过接下来,我先不介绍 CJSX,先行介绍一些的其他的在 React 中使用 CoffeeScript 的姿势。

react-coffee

为了让 CoffeeScript 能舒服的用上 React,最简单的方法莫过于把 React DOM 封装成一个库,然后直接调用。这么做的不止 coffee-react 一家,不过 coffee-react 挺具有代表性的,所以我们就拿他来说。以及这个名字和我们主要要介绍的那个库很像,不要搞错了!先来看看官方给出的一段代码示例:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{div}= React.DOM
module.exports= React.Component.toComponent class MyComponent
render: ->
(div null,
"My Component!"
)
{div}= React.DOM module.exports= React.Component.toComponent class MyComponent render: -> (div null, "My Component!" )
{div}= React.DOM

module.exports= React.Component.toComponent class MyComponent

 render: ->
 (div null,
 "My Component!"
 )

不评论风格美丑,但是要论优雅程度,我觉得还差那么点 w

coffee-react

这篇文章的主角就是这个了。直接来看看官方的示例吧:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
NeatComponent = React.createClass
render: ->
<div className="neat-component">
{<h1>A Component is I</h1> if @props.showTitle}
<hr />
{<p key={n}>This line has been printed {n} times</p> for n in [1..5]}
</div>
NeatComponent = React.createClass render: -> <div className="neat-component"> {<h1>A Component is I</h1> if @props.showTitle} <hr /> {<p key={n}>This line has been printed {n} times</p> for n in [1..5]} </div>
NeatComponent = React.createClass
  render: ->
    <div className="neat-component">
      {<h1>A Component is I</h1> if @props.showTitle}
      <hr />
      {<p key={n}>This line has been printed {n} times</p> for n in [1..5]}
    </div>

简直就是 Coffee 中的 JSX 啊!不过可惜的是,这样就需要再编译了。不过其实也无所谓,毕竟 cs 本身也要编译成 js 的。不过这里要说明下,因为这篇文章的写作开始的很早,一切都发生了变化…… 首先就是,这个库停止维护了。目前能达到此功能且依旧在维护的是:https://github.com/jsdf/cjsx-codemod。

cjsx-in-browser

这个库是用来在浏览器中直接调试 cjsx 的,把官网的例子重新写一下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>233333333333</title>
</head>
<body>
<div id="example">
</div>
<script type="text/cjsx">
HelloWorldApp = React.createClass
getInitialState: ->
clock: 0
componentWillMount: ->
setInterval =>
@setState clock: @state.clock+1
, 1000
resetClock: ->
@setState clock: 0
render: ->
<div className="hi">
<h1>Hello, world!</h1>
<p>Seconds elapsed: {@state.clock}</p>
<button
onClick={@resetClock}
disabled={if @state.clock < 2 then 'disabled' else ''}>
Reset
</button>
<ul>
{ <li>{i}</li> for i in [0...@props.listLength] }
</ul>
</div>
React.renderComponent <HelloWorldApp listLength=9 />, document.getElementById('example')
</script>
<script type="text/javascript" src="react-0.11.1.js"></script>
<script type="text/javascript" src="https://rawgit.com/iamdanfox/cjsx-in-browser/master/cjsx-in-browser.js"></script>
</body>
</html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>233333333333</title> </head> <body> <div id="example"> </div> <script type="text/cjsx"> HelloWorldApp = React.createClass getInitialState: -> clock: 0 componentWillMount: -> setInterval => @setState clock: @state.clock+1 , 1000 resetClock: -> @setState clock: 0 render: -> <div className="hi"> <h1>Hello, world!</h1> <p>Seconds elapsed: {@state.clock}</p> <button onClick={@resetClock} disabled={if @state.clock < 2 then 'disabled' else ''}> Reset </button> <ul> { <li>{i}</li> for i in [0...@props.listLength] } </ul> </div> React.renderComponent <HelloWorldApp listLength=9 />, document.getElementById('example') </script> <script type="text/javascript" src="react-0.11.1.js"></script> <script type="text/javascript" src="https://rawgit.com/iamdanfox/cjsx-in-browser/master/cjsx-in-browser.js"></script> </body> </html>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>233333333333</title>
</head>
<body>
<div id="example">
</div>

<script type="text/cjsx">
HelloWorldApp = React.createClass
  getInitialState: ->
    clock: 0
    
  componentWillMount: ->
    setInterval =>
        @setState clock: @state.clock+1
    , 1000
  
  resetClock: ->
    @setState clock: 0
    
  render: ->
    <div className="hi">
      <h1>Hello, world!</h1>
      <p>Seconds elapsed: {@state.clock}</p>
      <button 
        onClick={@resetClock} 
        disabled={if @state.clock < 2 then 'disabled' else ''}>
      Reset
      </button>
      <ul>
        { <li>{i}</li> for i in [0...@props.listLength]  }
      </ul>
    </div>

React.renderComponent <HelloWorldApp listLength=9 />, document.getElementById('example')
</script>
<script type="text/javascript" src="react-0.11.1.js"></script>
<script type="text/javascript" src="https://rawgit.com/iamdanfox/cjsx-in-browser/master/cjsx-in-browser.js"></script>
</body>
</html>

运行效果大致如下:

这是编译时的中间结果:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
HelloWorldApp = React.createClass
getInitialState: ->
clock: 0
componentWillMount: ->
setInterval =>
@setState clock: @state.clock+1
, 1000
resetClock: ->
@setState clock: 0
render: ->
React.DOM.div({"className": "hi"},
React.DOM.h1(null, "Hello, world!"),
React.DOM.p(null, "Seconds elapsed: ", (@state.clock)),
React.DOM.button({ \
"onClick": (@resetClock), \
"disabled": (if @state.clock < 2 then 'disabled' else '')}, """
Reset
"""),
React.DOM.ul(null,
( React.DOM.li(null, (i)) for i in [0...@props.listLength] )
)
)
React.renderComponent HelloWorldApp({"listLength": 9}), document.getElementById('example')
HelloWorldApp = React.createClass getInitialState: -> clock: 0 componentWillMount: -> setInterval => @setState clock: @state.clock+1 , 1000 resetClock: -> @setState clock: 0 render: -> React.DOM.div({"className": "hi"}, React.DOM.h1(null, "Hello, world!"), React.DOM.p(null, "Seconds elapsed: ", (@state.clock)), React.DOM.button({ \ "onClick": (@resetClock), \ "disabled": (if @state.clock < 2 then 'disabled' else '')}, """ Reset """), React.DOM.ul(null, ( React.DOM.li(null, (i)) for i in [0...@props.listLength] ) ) ) React.renderComponent HelloWorldApp({"listLength": 9}), document.getElementById('example')
HelloWorldApp = React.createClass
  getInitialState: ->
    clock: 0
    
  componentWillMount: ->
    setInterval =>
        @setState clock: @state.clock+1
    , 1000
  
  resetClock: ->
    @setState clock: 0
    
  render: ->
    React.DOM.div({"className": "hi"}, 
      React.DOM.h1(null, "Hello, world!"), 
      React.DOM.p(null, "Seconds elapsed: ", (@state.clock)), 
      React.DOM.button({  \
        "onClick": (@resetClock),   \
        "disabled": (if @state.clock < 2 then 'disabled' else '')}, """
      Reset
"""), 
      React.DOM.ul(null, 
        ( React.DOM.li(null, (i)) for i in [0...@props.listLength]  )
      )
    )

React.renderComponent HelloWorldApp({"listLength": 9}), document.getElementById('example')

这是最后的状态:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
(function() {
var HelloWorldApp;
HelloWorldApp = React.createClass({
getInitialState: function() {
return {
clock: 0
};
},
componentWillMount: function() {
return setInterval((function(_this) {
return function() {
return _this.setState({
clock: _this.state.clock + 1
});
};
})(this), 1000);
},
resetClock: function() {
return this.setState({
clock: 0
});
},
render: function() {
var i;
return React.DOM.div({
"className": "hi"
}, React.DOM.h1(null, "Hello, world!"), React.DOM.p(null, "Seconds elapsed: ", this.state.clock), React.DOM.button({
"onClick": this.resetClock,
"disabled": (this.state.clock < 2 ? 'disabled' : '')
}, "Reset"), React.DOM.ul(null, (function() {
var _i, _ref, _results;
_results = [];
for (i = _i = 0, _ref = this.props.listLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
_results.push(React.DOM.li(null, i));
}
return _results;
}).call(this)));
}
});
React.renderComponent(HelloWorldApp({
"listLength": 9
}), document.getElementById('example'));
}).call(this);
(function() { var HelloWorldApp; HelloWorldApp = React.createClass({ getInitialState: function() { return { clock: 0 }; }, componentWillMount: function() { return setInterval((function(_this) { return function() { return _this.setState({ clock: _this.state.clock + 1 }); }; })(this), 1000); }, resetClock: function() { return this.setState({ clock: 0 }); }, render: function() { var i; return React.DOM.div({ "className": "hi" }, React.DOM.h1(null, "Hello, world!"), React.DOM.p(null, "Seconds elapsed: ", this.state.clock), React.DOM.button({ "onClick": this.resetClock, "disabled": (this.state.clock < 2 ? 'disabled' : '') }, "Reset"), React.DOM.ul(null, (function() { var _i, _ref, _results; _results = []; for (i = _i = 0, _ref = this.props.listLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { _results.push(React.DOM.li(null, i)); } return _results; }).call(this))); } }); React.renderComponent(HelloWorldApp({ "listLength": 9 }), document.getElementById('example')); }).call(this);
(function() {
  var HelloWorldApp;

  HelloWorldApp = React.createClass({
    getInitialState: function() {
      return {
        clock: 0
      };
    },
    componentWillMount: function() {
      return setInterval((function(_this) {
        return function() {
          return _this.setState({
            clock: _this.state.clock + 1
          });
        };
      })(this), 1000);
    },
    resetClock: function() {
      return this.setState({
        clock: 0
      });
    },
    render: function() {
      var i;
      return React.DOM.div({
        "className": "hi"
      }, React.DOM.h1(null, "Hello, world!"), React.DOM.p(null, "Seconds elapsed: ", this.state.clock), React.DOM.button({
        "onClick": this.resetClock,
        "disabled": (this.state.clock < 2 ? 'disabled' : '')
      }, "Reset"), React.DOM.ul(null, (function() {
        var _i, _ref, _results;
        _results = [];
        for (i = _i = 0, _ref = this.props.listLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
          _results.push(React.DOM.li(null, i));
        }
        return _results;
      }).call(this)));
    }
  });

  React.renderComponent(HelloWorldApp({
    "listLength": 9
  }), document.getElementById('example'));

}).call(this);

后记:CS2

在半个多月之前,CS2 发布了其第一个正式版本。其中一个重大更新就是 ——CS 原生支持像 JSX 这样的写法了。于是这篇文章就彻底的作废了。这里是官方传送门:http://coffeescript.org/v2/#jsx。关于 CS2 的其他更新,我会撰写新的博文。

分享到

KAAAsS

喜欢二次元的程序员,喜欢发发教程,或者偶尔开坑。(←然而并不打算填)

相关日志

  1. 没有图片
  2. 没有图片
  3. 没有图片
  4. 没有图片

评论

  1. 开发者头条 2017.08.28 1:32 下午

    感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/dg5jou 欢迎点赞支持!
    欢迎订阅《KAAAsS 的 J&A 教室》https://toutiao.io/subjects/69380

  2. 上海 seo 2017.10.06 6:15 下午

    代码很复杂的感觉。。

在此评论中不能使用 HTML 标签。