在使用 React 的時(shí)候,會(huì)出現(xiàn)幾個(gè)看起來(lái)相互混淆的概念,例如:Element,Class 和 Component。
我們先來(lái)看看 Element。
Element
Element 是后文 Class 的實(shí)例,React 通過解析每一個(gè)創(chuàng)建的 Element, 計(jì)算出需要對(duì) DOM 進(jìn)行的實(shí)際操作來(lái)完成渲染的。
React.render(
React.createElement('div', {}, 'Hello, world!'),
document.body
);
代碼中的 React.createElement 創(chuàng)建了一個(gè)新的 div Componnet 的實(shí)例。第一個(gè)參數(shù) “div”是 React 預(yù)先定義好的。
第二個(gè)參數(shù){} 是需要傳入的 props,第三個(gè)參數(shù)是 "Child Element"。
當(dāng)你創(chuàng)建了第一個(gè) Element,其所有的“孩子”都會(huì)被自動(dòng)創(chuàng)建。
用 “Element” 來(lái)命名,應(yīng)該是遵從了 HTML Elements 的習(xí)慣。
Component 和 ReactClass
React 自己定義了好了大量的 Components,從 "div" 到 "svg",包含了幾乎所有 HTML Tags。
當(dāng)然,我們也可以創(chuàng)建自己的 Component,例如:
var MyComponent = React.createClass({
render: function() {
...
}
});
MyComponent 就是我們創(chuàng)建的 Component,至少需要包含一個(gè) render 方法的實(shí)現(xiàn)。隨后,我們就可以通過 React.createElement(MyComponent, {}, null) 來(lái)創(chuàng)建 “MyComponent” 的 “Element” 了。
這里最容易造成混淆的是,創(chuàng)建 “MyComponent” 方法名是 “React.createClass”,而不是 “React.createComponent”?!癱reateClass” 卻創(chuàng)建出了 “Component”,這是一個(gè)詭異的地方,雖然官方文檔定義 “createClass” 的返回類型為 “ReactClass”。
之前有過爭(zhēng)議 https://groups.google.com/forum/#!topic/reactjs/40dxGadNXeM.
Factory
為了簡(jiǎn)化 React.createElement 的調(diào)用語(yǔ)法,React.createFactory 被引入:
var div = React.createFactory('div');
var root = div({ className: 'my-div' });
React.render(root, document.getElementById('example'));
React.createFactory 的定義基本就是如下形式,Element 的類型被提前綁定了。
function createFactory(type) {
return React.createElement.bind(null, type);
}
React.DOM.div 等都是預(yù)先定義好的 “Factory”?!癋actory” 用于創(chuàng)建特定 “ReactClass” 的 “Element”。
用 ES6 Class 代替 React.createClass
從 React 0.13 開始,可以使用 ES6 Class 代替 React.createClass 了。這應(yīng)該是今后推薦的方法,但是目前對(duì)于 mixins 還沒有提供官方的解決方案,你可以利用第三方的實(shí)現(xiàn) https://github.com/brigand/react-mixin.
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
React.Component 是基類(得,這里又變成了 Component了,其實(shí)準(zhǔn)確的命名可能是 ElementClass,或者 ComponentClass,不過這樣太長(zhǎng)了??)。
React.createClass 中的某些工作,可以直接在 ES6 Class 的構(gòu)造函數(shù)中來(lái)完成,例如:getInitialState 的工作可以被構(gòu)造函數(shù)所替代:
export class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div onClick={this.tick.bind(this)}>
Clicks: {this.state.count}
</div>
);
}
}
propTypes 和 getDefaultTypes 這種靜態(tài)設(shè)置則可以變成類級(jí)別(不是類實(shí)例成員)的定義。
var ExampleComponent = React.createClass({ ... });
ExampleComponent.propTypes = {
aStringProp: React.PropTypes.string
};
ExampleComponent.defaultProps = {
aStringProp: ''
};
另外,原本通過 React.createClass 創(chuàng)建的 Component/Element 中的成員函數(shù)都是有 auto binding 能力的(缺省綁定到當(dāng)前 Element),那么使用 ES6 Class 則需要你自己綁定,或者使用 => (Fat Arrow)的聲明方式。Fat Arrow 會(huì)缺省將當(dāng)前 this 綁定到當(dāng)前類實(shí)例(ES6 和 CoffeeScript 一樣),但是不要對(duì) render 使用 =>。
class Counter extends React.Component {
tick = () => {
...
}
...
}
CoffeeScript 天生從這種新方式中得益,例如:
div = React.createFactory 'div'
class Counter extends React.Component
@propTypes = initialCount: React.PropTypes.number
@defaultProps = initialCount: 0
constructor: (props) ->
super props
@state = count: props.initialCount
tick: =>
@setState count: @state.count + 1
render: ->
div onClick: @tick,
'Clicks: ' + @state.count
CoffeeScript 一直是最簡(jiǎn)短的 JS。