一、 前言
隨著 react 最新的一個大版本中,給我們帶來了 Hooks:React v16.8: The One With Hooks,從而將 Function component 的能力提高了一大截,成功的擁有了可以與 Class component 抗衡的能力。但話說回來,雖然 Hooks 看起來很美好,最近也有不少文章都講解了Hooks這一“黑魔法”,但技術(shù)的不斷演進,本身就是一個解決以往所存在問題的過程,因此我個人認為著眼于現(xiàn)在,回望過去,去看一看 react component 的發(fā)展之路,去看看 Class component 以及 Function component 為什么會出現(xiàn)以及它們出現(xiàn)的意義,所要解決的問題,也對于我們?nèi)媪私?react 是很有幫助的。
從 react component 的發(fā)展歷程上來看,它主要是經(jīng)歷了一下三個階段:
- createClass Component
- Class Component
- Function Component
這個三個階段也是react的組件不斷走向輕量級的一個過程。其中 Class Component 完全替代了 createClass Component 成為了現(xiàn)在我們開發(fā) react 組件的主流,而 Function Component 也在 Hooks 推出后磨刀霍霍,準備大干一場。下面就讓我們?nèi)タ纯慈叩木唧w情況吧~
注:這篇文章整體只是對React Component的發(fā)展歷程的一個概括或者說是我自己學習后的一個整理,想要詳細了解,還請看看我在文章貼的那些鏈接。
二、 createClass Component
說實話,createClass Component 我也沒用過,因為我接觸到 react 的時候已經(jīng)是2017年下半年了,那時候 ES6 已經(jīng)大行其道,class component 也已經(jīng)完全取代了 createClass Component。但現(xiàn)在看來 createClass Component 的語法也很簡單,并不復雜:
import React from 'react'
const MyComponent = React.createClass({
// 通過proTypes對象和getDefaultProps()方法來設(shè)置和獲取props
propTypes: {
name: React.PropTypes.string
},
getDefaultProps() {
return {
}
},
// 通過getInitialState()方法返回一個包含初始值的對象
getInitialState(){
return {
sayHello: 'Hello Srtian'
}
}
render() {
return (
<p></p>
)
}
})
export default MyComponent
react.createClass的語法并不復雜,它通過 createClass 來創(chuàng)建一個組件,并通過propTypes和getDefaultProps來獲取props,通過通過getInitialState()方法返回一個包含初始值的對象,雖然從現(xiàn)在看來還是有點麻煩,但總體上來看代碼也比較清晰,跟現(xiàn)在的 Class Component差別并不是太大。但 react.createClass 自從 react 15.5版本就不再為 react 官方所推介,而是想讓大家的使用 class component 來代替它。而且在 react 16版本發(fā)布后,createClass 更是被廢棄,當我們使用它的時候,會提示報錯,也就是說,在 react 團隊看來 createClass 已經(jīng)完全沒有存在的必要了。
其實 Class Component 完全替代 React.createClass 并不是說 React.createClass 有多壞,相反它還有一些 class Component 所沒有的特性。它的廢棄是由于ES6的出現(xiàn),新增了 class 這一語法糖,讓我們在 JavaScript 的開發(fā)中可以直接使用 extends 來擴展我們的對象,因此為了與標準的ES6接軌,原有的只在 react 中使用的 createClass 自然而然也成為了被拋棄的對象。但 class Component 在剛出現(xiàn)的時候也仍然存在的不小的爭議,因為這兩者還是存在一定的差別的,比如當時在Stack Overflow便出現(xiàn)了關(guān)于這兩者的討論,感興趣的朋友可以去看看:
https://stackoverflow.com/questions/30668464/react-component-vs-react-createclass
總的來說,除了語法上存在差異外,Class Component 和 React.createClass 的區(qū)別主要是以下兩點(詳情可以看看上面的回答):
- React.createClass 會正確綁定 this,而 React.Component 則不行,我們需要在 constructor 里面使用 bind 或者直接使用箭頭函數(shù)來綁定 this。
- React.Component 不能使用 React mixins 特性,這一方面我們可以使用高階組件來彌補。
三、Class Component
Class Component創(chuàng)建的方式也很簡單,就是普通的ES6的class的語法,通過extends來創(chuàng)建一個新的對象來創(chuàng)建react組件,下面是使用class Component創(chuàng)建一個組件的例子(由于為了給后面聊一聊hooks,所以在這里我使用了antd的例子)
class Modal extends React.Component {
state = { visible: false }
showModal = () => {
this.setState({
visible: true,
});
}
handleOk = (e) => {
console.log(e);
this.setState({
visible: false,
});
}
handleCancel = (e) => {
console.log(e);
this.setState({
visible: false,
});
}
render() {
return (
<div>
<Button type="primary" onClick={this.showModal}>
Open Modal
</Button>
<Modal
title="Basic Modal"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<p>this is a modal</p>
</Modal>
</div>
);
}
}
上面就是antd中一個簡單的 modal 組件的例子,其內(nèi)部就是通過維護 visible 的狀態(tài)來控制這個 modal 是否顯示。我們可以看到,其中的一些方法都是使用箭頭函數(shù)的方式來將 this 綁定到正確的屬性。(具體為什么要這么做,不清楚的朋友可以看看下面這篇文章:)
而類似于上面的這種組件,也是近兩年來我們在日常開發(fā)中使用最多的組件開發(fā)的方式。那為什么到了現(xiàn)在,我們又開始要強調(diào)使用 Function Component 來進行開發(fā)了呢?主要是由于 Class Component 所開發(fā)的組件仍然存在以下一些問題:
- this 綁定的問題:
我們前面也提到了,我們在使用原本的 React.createClass 時并不需要去考慮this綁定的問題,而現(xiàn)在我們卻要時刻注意使用bind或者箭頭函數(shù)來讓this正確綁定,同時也讓一些新上手react的同學的上手成本有所提升。雖然這不是React的鍋,但這方面的問題仍然客觀存在。 - 嵌套地獄: 這種情況則多發(fā)生于需要用到Context的場景下,在這種場景下,數(shù)據(jù)是同步的,因為需要通知更新所有有引用到數(shù)據(jù)的地方,因此我們就需要通過render-props 的形式定義在Context.Consumer的children中,而使用到越多的Context 就會導致嵌套層級越多,這很容易讓人看代碼看的一臉懵逼。比如這樣:
<FirstContext.Consumer>
{first => (
<SecondContext.Consumer>
{second => (
<ThirdContext.Consumer>
{third => (
<Component />
)}
</ThirdContext.Consumer>
)}
</SecondContext.Consumer>
)}
</FirstContext.Consumer>
- Life-cycles 的問題:生命周期函數(shù)也是我們在日常開發(fā)所經(jīng)常使用到的東西。雖然生命周期函數(shù)用起來很方便,但一旦組件的邏輯變得復雜起來,這些生命周期函數(shù)也會變得難以理解和維護;同時如何讓這些生命周期函數(shù)與react渲染有效結(jié)合也是一個不小的問題,這往往可能會讓一些剛上手的人摸不著頭腦。此外使用這些生命周期函數(shù)時也可能會出現(xiàn)一些預料之外的事情發(fā)生(比如在某些生命周期函數(shù)中進行數(shù)據(jù)請求,而導致組件被重復渲染多次的問題等等,這些都是有可能發(fā)生的)
詳細可以去看看知乎上的這個回答:https://www.zhihu.com/question/300049718
四、Function Component
看到這里,大家對class Component所存在的一些問題也算是有一些了解了,但為什么它還能橫行如此之久,一直占據(jù)著主流的地位呢?其本質(zhì)上就是因為沒有競爭對手嘛,F(xiàn)unction Component 長期沒有內(nèi)部狀態(tài)管理機制,只能通過外部來管理狀態(tài),因此組件的可測試性非常的高,寫起來也簡潔明了,符合現(xiàn)在前端函數(shù)式的大潮流,是個好同志。但也正是因為沒有狀態(tài)管理機制,所以無法和Class Component相抗衡,畢竟一旦組件內(nèi)部的邏輯變得復雜之后,內(nèi)部的狀態(tài)管理機制是必須的。
因此 React 團隊基于 Function Component 提出 Hooks 的概念,用以解決 Function Component 的內(nèi)部狀態(tài)管理,同時也希望通過 Hooks 來解決 Class Component 所存在的問題。下面就是使用 Hooks 針對 antd 中的 modal 進行的改寫,大家可以自行感受一下:
const Modal = () => {
const [visible , changeVisible] = useState(false)
return (
<div>
<Button type="primary" onClick={()=>changeVisible(true)}>open</Button>
<Modal
title="Basic Modal"
visible={visible}
onOk={()=>changeVisible(false)}
onCancel={()=>changeVisible(false)}
>
<p>this is a modal</p>
</Modal>
</div>
)
}
我們可以看到,基于 Function Component 與 Hooks 所編寫出來的組件代碼是相當簡潔明了的,也直接避免了我們上面所提到的 this 指向的問題。而對于上面所提到的嵌套地獄以及 Life-cycles 的問題,Hooks也提供了 useContext 和 useEffect(這個倒還是存在一些問題) 來解決,在這里我也不詳細說了,詳情可以去看官方文檔或者是 Dan 的博客:
好了,看到這里我想大家都以為上面 Class Component 的問題都已經(jīng)得到圓滿解決了,F(xiàn)unction Component好像已經(jīng)圓滿了,我們只管放心的使用它就好了。但世界上哪有這么好的事情,F(xiàn)unction Component 仍然存在著下面幾個 tip 是我們在使用前要知道的:
- Function Component 與 Class Component 表現(xiàn)不同,這塊不清楚的可以直接去看Dan的文章,他對這方面做了很明白的闡述:
https://overreacted.io/how-are-function-components-different-from-classes/
- 使用useState需要注意的是,它的執(zhí)行順序要在每次 render 時必須保持一致,不可以進判斷和循環(huán),必須寫在最前面,關(guān)于這一點看視頻:
- Function Component 中,外部對與函數(shù)式組件的操作只能通過 props 來進行控制,不能通過函數(shù)式組件內(nèi)部暴露方法來對組件進行操作。
參考資料:
- https://ultimatecourses.com/blog/react-create-class-versus-component
- https://overreacted.io/how-are-function-components-different-from-classes/
- https://www.youtube.com/watch?v=dpw9EHDh2bM
- https://overreacted.io/a-complete-guide-to-useeffect/
- https://www.zhihu.com/question/300049718
- https://stackoverflow.com/questions/30668464/react-component-vs-react-createclass
- http://taobaofed.org/blog/2018/11/27/hooks-and-function-component/
- https://www.freecodecamp.org/news/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb/