寫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的區別