写React的时候,你是应该用React.createClass
语法还是ES6的class
语法呢?或者都不?这篇文章将会解释一些二者之间的差异来帮助你做选择。
React可以用ES5或ES6完美书写。
使用JSX意味着你将通过Babel将JSX通过“build”转化为React.createElement
调用。很多人利用这个特点在Babel的编译列表中添加一些es2015
的语法以此来让ES6完全可用。
如果你正在使用Quik或是React Heatpack,这已经为你配置好了(如果你还没有安装环境,请阅读quick start React)。
比较 createClass 和 class
以下是使用React.createClass
和ES6的class
书写相同的组件:
var InputControlES5 = React.createClass({
propTypes: {
initialValue: React.PropTypes.string
},
defaultProps: {
initialValue: ''
}, // Set up initial state
getInitialState: function() {
return {
text: this.props.initialValue || 'placeholder'
};
},
handleChange: function(event) {
this.setState({
text: event.target.value
});
},
render: function() {
return (
<div>
Type something:
<input onChange={this.handleChange}
value={this.state.text} />
</div>
);
}
});
class InputControlES6 extends React.Component {
constructor(props) {
super(props);
// Set up initial state
this.state = {
text: props.initialValue || 'placeholder'
};
// Functions must be bound manually with ES6 classes
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
text: event.target.value
});
}
render() {
return (
<div>
Type something:
<input onChange={this.handleChange}
value={this.state.text} />
</div>
);
}
}
InputControlES6.propTypes = {
initialValue: React.PropTypes.string
};
InputControlES6.defaultProps = {
initialValue: ''
};
以下是一些关键的不同点:
函数的绑定
这或许是最大的一个变化点
使用createClass
, 每一个函数属性都会被React自动绑定。指的是this.whateverFn
这样的函数在任何你需要调用的时候都会自动为你绑定正确的this
。
在ES6的class
中,就不同了: 函数不再被自动绑定。你需要手动去绑定它们。最好的地方就是和以上例子一样,在构造函数里。
如果你不想非要手动打印所有的绑定操作,也可以通过react-autobind
或者autobind-decorator
来检查。
另一种方式就是在你使用的地方通过内联来绑定:
// Use `.bind`:
render() {
return (
<input onChange={this.handleChange.bind(this)}
value={this.state.text} />
);
}
// --- OR ---
// Use an arrow function:
render() {
return (
<input onChange={() => this.handleChange()}
value={this.state.text} />
);
}
以上任意一种都可以,然而在效率上却不行了。每一次调用render
(可以说是非常频繁!)一个新的函数都会被创建。与在构造函数里只绑定一次相比就慢一些。
最终的选择是使用箭头函数直接替换函数在类中的声明,像这样:
// the normal way
// requires binding elsewhere
handleChange(event) {
this.setState({
text: event.target.value
});
}
// the ES7 way
// all done, no binding required
handleChange = (event) => {
this.setState({
text: event.target.value
});
}
通过这种方式,你不需要绑定任何东西。这都已经通过神奇的箭头函数被搞定了。像期望的那样,函数内部的this
将会指向组件实例。
唯一的注意的地方就是这仅是一个"试验性" 的特性,意味着这不是官方的ES6规范。但是这依旧可以通过设置babel为"stage-0"来支持。如果你喜欢这种语法(被称作“把handleChange设置为带event的箭头函数”),试试吧。
构造函数应该调用super
ES6类的constructor需要接收props
并且调用super(props)
。这是createClass
所没有的一点。
class和createClass的比较
这个很明显,一个调用React.createClass
并传入一个对象,另一个则是使用class
继承React.Component
。
小提示: 导入Component
时如果在一个文件里包含多个组件,可以直接通过以下方式节省一些代码量:
import React, {Component} from 'react'
初始化State的设置
createClass
接受一个只会在组件被挂载时才会调用一次的initialState
函数。
ES6 的class
则使用构造函数。在调用super
之后,可以直接设置state。
propTypes 和 defaultProps 的设置
使用createClass
时,在传入的对象上定义一个propTypes
和defaultProps
作为属性。
使用ES6的类,这些将会成为类本身的属性,因此它们可以在类定义之后补充上。
如果你开启了ES7属性初始化,这里还有些捷径:
class Person extends React.Component {
static propTypes = {
name: React.PropTypes.string,
age: React.PropTypes.string
};
static defaultProps = {
name: '',
age: -1
};
...
}
第三种选择
除createClass
和class
以外,React也支持“无状态函数式组件”。基本上,这仅是一个不能有state的函数,并且它不能使用像componentWillMount
或shouldComponentUpdate
这样的任何生命周期方法。无状态函数式组件对仅仅接受并依赖props渲染的简单组件来讲很棒,以下是几个例子:
function Person({firstName, lastName}) {
return (
<span>{lastName}, {firstName}</span>
);
}
这利用ES6的解构将传入的props分解开来,但也可以这么写:
function Person(props) {
var firstName = props.firstName;
var lastName = props.lastName;
return (
<span>{lastName}, {firstName}</span>
);
}
用哪个才对呢?
Facebook已经声明了React.createClass将最终会被ES6的classes替代,但是他们也说“在我们找到当前mixin所使用的例子的替代者以及在语言上支持类属性的初始化器前,我们不打算废弃React.createClass”。
在任何可以使用无状态函数式组件的地方使用它。简单而且会强制性地简化你的组件。
对于一些需要state,生命周期方法或是通过ref来操作潜在的DOM节点的复杂组件来讲,请使用class。
尽管知道这三种写法风格很棒,但在StackOverflow或者其他地方查找解决方案时可能仍会看到一些混杂着ES5和ES6写法的答案。ES6风格已经非常流行了但这不会是你看到的唯一一种写法。
From:React: ES5 (createClass) 还是 ES6 (class)? (译)
可参考:createClass 和 extends Component的区别