###什么是生命周期函數
1. 組件中在某個階段會自動執行的函數。
- 比如我們執行使用render函數,在prop或者state變化時,render函數自動執行。
- 因此render函數就是一個生命周期函數。
2. constructor在組件創建的時候也會自動調用。但是他不是react獨有,是es6中的函數所以,我們不將他列為生命周期函數。
###生命周期分為4個階段
1. initialization(組件初始化)
- 我們在創建組件的時候需要繼承react Component這個基類,也就繼承這個react的基類,才能有render(),生命周期等方法可以使用,這也說明為什么函數組件不能使用這些方法的原因。同時也讓組件能使用setState方法。
- 然后我們在 constructor 構造函數中使用 super(props)來將父組件傳遞的props注入給這個組件,以及使用this.state初始化這個組件的屬性。
- 這一系列動作就是組件的初始化。
2. mount (組件的掛載)
- 此階段分為三個時期
1. componentWillMount(掛載之前)
- 這個函數在組件掛載到DOM之前的時候執行,所以你在這里引用setState方法,是不會引起組件的重新渲染。
- 同樣這里做的事情如果放在constructor構造函數中去使用也是可以的。
- 這個函數只會被調用一次,就是組件掛載到DOM之前的時候。其他時候是不會觸發這個函數的。
2. render(掛載中)
- 根據組件的props和state是否變化,變化即執行,這里的變化要注意,並不是值變化。只要重新賦值,新舊值相同也會執行。
- 然后return 一個React元素(描述組件,即UI),不負責組件實際渲染工作,之后由React自身根據此元素去渲染出頁面DOM。render是純函數(Pure function:函數的返回結果只依賴於它的參數;函數執行過程里面沒有副作用。
- 不能在里面執行this.setState,會有改變組件狀態的副作用。
3. componentDidMount(掛載完成)
- 約定將ajax請求放在這個生命周期函數中
- 組件掛載到DOM后被執行,只會執行一次。
3. update (組件更新時)
- 首先我們來了解一下什么時組件更新。只有在通過setState函數使sate和props變化或重新賦值時才叫組件更新。
- setState引起父組件的render函數執行,同時也會引起它的子組件的render函數執行。
- 原因是react虛擬DOM的diff算法,同級比較原理。
- 只要重新賦值就是組件更新,如果值並沒有變,也更新組件,這樣就會耗性能,也是我們講同級比較的時候說的一個弊端。
- 接下來我們了解一下有那些周期函數
1. componentWillReceiveProps(nextProps)
- 此方法只調用於props引起的組件更新過程中,參數nextProps是父組件傳給當前組件的新props。但父組件render方法的調用不能保證重傳給當前組件的props是有變化的,所以在此方法中根據nextProps和this.props來查明重傳的props是否改變,以及如果改變了要執行啥,比如根據新的props調用this.setState出發當前組件的重新render
2. shouldComponentUpdate(nextProps, nextState)
- 此方法通過比較nextProps,nextState及當前組件的this.props,this.state,返回true時當前組件將繼續執行更新過程,返回false則當前組件更新停止,以此可用來減少組件的不必要渲染,優化組件性能。ps:這邊也可以看出,就算componentWillReceiveProps()中執行了this.setState,更新了state,但在render前(如shouldComponentUpdate,componentWillUpdate),this.state依然指向更新前的state,不然nextState及當前組件的this.state的對比就一直是true了。
3. componentWillUpdate(nextProps, nextState)
- 此方法在調用render方法前執行,在這邊可執行一些組件更新發生前的工作,一般較少用。
4. render
- render方法在上文講過,這邊只是重新調用。
5. componentDidUpdate(prevProps, prevState)
- 此方法在組件更新后被調用,可以操作組件更新的DOM,prevProps和prevState這兩個參數指的是組件更新前的props和state
- 優化弊端
- 1.當因為父組件重新render,使得props重新被賦值,導致子組件跟着渲染.
/** * 方法一:解決上述弊端,可以在shouldComponentUpdate函數,組件更新前進行判斷,props是否改變,再確定是否執行重新渲染。 */ class Child extends Component { shouldComponentUpdate(nextProps){ if(nextProps.value === this.props.value){ return false } return true } render() { return <div>{this.props.value}</div> } } /** * 方法二: * 1.解決上述弊端,也可以先將this.props.value賦值給子組件的state * 2.再在componentWillReceiveProps函數中,使用setState去重新給stae中的屬性賦值this.props.value * 3.文檔中提到,在該函數(componentWillReceiveProps)中調用 this.setState() 將不會引起第二次渲染。 * 4.因為componentWillReceiveProps是在props有變化的時候才會觸發,所以在這里面做this.setState()一定是有改變state */ // class Child extends Component { constructor(props) { super(props); this.state = { value: props.value // 先將this.props.value賦值給子組件的state }; } componentWillReceiveProps(nextProps) { // 在props有變化的時候才會觸發這個方法 this.setState({value: nextProps.value}); // 重新賦值,引起render } render() { return <div>{this.state.value}</div> } }
- 2.組件本身調用setState方法,但是並沒有改變state中的值。
/** * 也是通過shouldComponentUpdate判斷新舊值是否改變,改變才做render */ class Test extends Component { constructor(props) { super(props); this.state = { value:1 } } shouldComponentUpdate(nextState){ // 應該使用這個方法判斷新舊值是否改變 if(nextState.value === this.state.value){ return false // 沒有改變返回false } return true } changeState = () => { // 雖然調用了setState ,但state並無變化 const value = this.state.value this.setState({ value }) } render() { return <div onClick = {this.changeState}>{this.state.value}</div> } }
4. 卸載階段(componentWillUnmount)
- 只有一個生命周期方法componentWillUnmount
- 此方法在組件被卸載前調用,可以在這里執行一些清理工作,比如清楚組件中使用的定時器,清楚componentDidMount中手動創建的DOM元素等,以避免引起內存泄漏。
5. 在react組件中,除了render函數,其他任何生命周期函數都可以不寫,因為組件繼承了react中的 Component,Component內置了其他的生命周期函數