2. React組件生命周期
-
2.1. 認識生命周期
- 2.1.1. 很多事物都有從創建到銷毀的整個過程,這個過程稱之為生命周期;
- 2.1.2. React組件也有自己的生命周期,了解生命周期可以讓我們在最合適的地方完成想要的功能
- 2.1.3. 生命周期和生命周期函數的關系:
- 2.1.3.1. 生命周期是一個抽象的概念,在生命周期的整個過程,分成了很多個階段;
- 比如掛載階段(
Mount
), 組件第一次在DOM樹中被渲染的過程; - 比如更新階段(
Update
), 組件狀態發生變化時,重新更新渲染的過程; - 比如卸載階段(
Unmount
), 組件從DOM樹中移除的過程;
- 比如掛載階段(
- 2.1.3.2. React內部為了告訴我們當前處于那些階段,會
對組件內部實現的某些函數進行回調
,這些函數就是生命周期函數
;- 比如實現
componentDidMount函數
:組件已經掛載到DOM上
時,就會回調; - 比如實現
componentDidUpdate函數
:組件已經更新
時,就會回調; - 比如實現
componentWillUnmount函數
:組件即將被移除
時,就會回調; - 可以在這些回調函數中編寫自己的邏輯代碼,來完成自己的需求功能;
- 比如實現
- 2.1.3.1. 生命周期是一個抽象的概念,在生命周期的整個過程,分成了很多個階段;
- 2.1.4. React生命周期時,主要指的是類的生命周期,因為函數式組件是沒有生命周期函數的(后面可以通過hooks來模擬一些生命周期的回調,寫到hooks會在進行記錄)
-
2.2. 生命周期解析
-
2.2.1. 先來了解下最基礎、最常用的生命周期函數:
compentDidMount
、componentDidUpdate
、componentWillUnmount
函數 -
2.2.2. 生命周期函數如圖:官網圖譜地址
-
2.2.3. 掛載階段:
componentDidMount
-
- 在掛載時,首先會創建一個組件實例,在創建實例的過程中,就會執行類中
constructor函數
(每次引入使用組件時,創建的是ReactElement
, 每次調用就創建一個組件實例, 每次創建實例時,就會先執行constructor函數
)
類 -> 類實例class Person {} -> 類const p1 = new Person() -> 類實例
- 在掛載時,首先會創建一個組件實例,在創建實例的過程中,就會執行類中
-
- 緊接著調用類中的
render方法
- 緊接著調用類中的
-
- 掛載完成后會調用的
componentDidMount函數
- 掛載完成后會調用的
-
- 如下圖:
- 如下圖:
-
- 示例代碼如下:
- HelloWorld.jsx
//import React from 'react';class HelloWorld extends React.Component {// 1.構造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}// 2.執行render方法render () {console.log('HelloWorld render')return (<div><h2>Hello World</h2><p>Hello World是程序員的第一個代碼</p></div>)}// 3.組件被渲染到DOM:被掛載到DOM上componentDidMount () {console.log('HelloWorld componentDidMount')}}export default HelloWorld;
- App.jsx
import React from "react";import HelloWorld from "./HelloWorld";class App extends React.Component {render () {return (<div>哈哈哈哈{/* 組件每引用一次,就創建一個實例,每創建一次組件實例,就會先執行constructor函數 */}<HelloWorld /><HelloWorld />{/* <HelloWorld /><HelloWorld /> */}</div>)}}export default App;
-
-
2.2.4. 更新階段:componentDidUpdate
-
- 當數據發生更新時,執行setState方法
-
- 執行setState方法后,重新渲染頁面, 重新執行render方法,它會根據最新修改之后的message,重新生成一個新的樹,然后在進行diff算法,然后決定要更新那部分
-
- react會更新dom和refs,并調用componentDidUpdate方法
-
- 如下圖:
- 如下圖:
-
- 關鍵代碼如下;
// 4.組件的DOM被更新完成:DOM發生更新componentDidUpdate () {/*** * - 1. 當數據發生更新時,執行setState方法* - 2. 執行setState方法后,重新渲染頁面, 重新執行render方法,它會根據最新修改之后的message,重新生成一個新的樹,然后在進行diff算法,然后決定要更新那部分* - 3. react會更新dom和refs,并調用componentDidUpdate方法* * * */console.log('HelloWorld componentDidUpdate')}
- 詳細代碼如下:
import React from 'react';class HelloWorld extends React.Component {// 1.構造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}changeText () {this.setState({ message: '你好啊 李銀河' })}// 2.執行render方法render () {const { message } = this.stateconsole.log('HelloWorld render')return (<div><h2>{message}</h2><button onClick={e => this.changeText()}>修改文本</button><p>{message}是程序員的第一個代碼</p></div>)}// 3.組件被渲染到DOM:被掛載到DOM上componentDidMount () {/*** * - 1. 在掛載時,首先會創建一個組件實例,在創建實例的過程中,就會執行類中`constructor函數`(每次引入使用組件時,創建的是ReactElement, 每次調用就創建一個組件實例, 每次創建實例時,就會先執行constructor函數)* - 2. 緊接著調用類中的`render方法`* - 3. 掛載完成后會調用的`componentDidMount函數`* * * */ console.log('HelloWorld componentDidMount')}// 4.組件的DOM被更新完成:DOM發生更新componentDidUpdate () {/*** * - 1. 當數據發生更新時,執行setState方法* - 2. 執行setState方法后,重新渲染頁面, 重新執行render方法,它會根據最新修改之后的message,重新生成一個新的樹,然后在進行diff算法,然后決定要更新那部分* - 3. react會更新dom和refs,并調用componentDidUpdate方法* * * */console.log('HelloWorld componentDidUpdate')}}export default HelloWorld;
- 關鍵代碼如下;
-
-
2.2.5. 卸載階段:
componentWillUnmount
-
- 當組件從 DOM 中移除時,會調用 componentWillUnmount() 方法。
-
- 如下圖:
- 如下圖:
-
- 示例代碼:
- 關鍵代碼:
// 5.組件從DOM中卸載掉:從DOM移除掉componentWillUnmount () {console.log('HelloWorld componentWillUnmount')}
-
- 完整代碼:
- HelloWorld.jsx
import React from 'react';class HelloWorld extends React.Component {// 1.構造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}changeText () {this.setState({ message: '你好啊 李銀河' })}// 2.執行render方法render () {const { message } = this.stateconsole.log('HelloWorld render')return (<div><h2>{message}</h2><p>{message}是程序員的第一個代碼</p><button onClick={e => this.changeText()}>修改文本</button></div>)}// 3.組件被渲染到DOM:被掛載到DOM上componentDidMount () {/*** * - 1. 在掛載時,首先會創建一個組件實例,在創建實例的過程中,就會執行類中`constructor函數`(每次引入使用組件時,創建的是ReactElement, 每次調用就創建一個組件實例, 每次創建實例時,就會先執行constructor函數)* - 2. 緊接著調用類中的`render方法`* - 3. 掛載完成后會調用的`componentDidMount函數`* * * */ console.log('HelloWorld componentDidMount')}// 4.組件的DOM被更新完成:DOM發生更新componentDidUpdate () {/*** * - 1. 當數據發生更新時,執行setState方法* - 2. 執行setState方法后,重新渲染頁面, 重新執行render方法,它會根據最新修改之后的message,重新生成一個新的樹,然后在進行diff算法,然后決定要更新那部分* - 3. react會更新dom和refs,并調用componentDidUpdate方法* * * */console.log('HelloWorld componentDidUpdate')}// 5.組件從DOM中卸載掉:從DOM移除掉componentWillUnmount () {console.log('HelloWorld componentWillUnmount')}}export default HelloWorld;
- App.jsx
import React from "react"; import HelloWorld from "./HelloWorld";class App extends React.Component {constructor () {super();this.state = {isSHowHw: true}}switchShowHw () {this.setState ({ isSHowHw: !this.state.isSHowHw })}render () {const { isSHowHw } = this.state;return (<div>哈哈哈哈{/* 組件每引用一次,就創建一個實例,每創建一次組件實例,就會先執行constructor函數 */}<button onClick={ e => this.switchShowHw() }>切換</button>{ isSHowHw && <HelloWorld />}</div>) } }export default App;
-
-
2.2.6. 開發中常用的是:
constructor()、render()、componentDidMount()、componentWillUnmount()
-
-
2.3. 生命周期函數
- 2.3.1.
constructor
-
- 如果不初始化state或不進行方法綁定,則不需要為React組件實現構造函數。
-
- constructor中通常只做兩件事:
- 通過給
this.state
賦值對戲那個初始化內部的state; - 為事件綁定實例(this);
-
- 2.3.2
componentDidMount
-
componentDidMount()會在組件掛載之后(插入DOM樹中)立即調用
-
componentDidMount
中通過進行那些操作?
- 2.1. 依賴于DOM的操作可以在這里進行;
- 2.2. 再次是發送網絡請求最好的地方(官方建議)
- 2.3. 可以在此處添加一些訂閱(在
componentWillUnmount
中取消訂閱,不然會內存泄漏)
-
- 2.3.3
componentDidUpdate
-
componentDidUpdate()
會在組件更新之后立即調用, 首次渲染不會執行此方法;
- 當組件更新后,可以在此處對DOM進行操作;
- 如果對更新前后的props進行了比較,也可以選擇在此處進行網絡請求(例如:當props未發生變化時,則不會執行網絡請求)
-
- 2.3.4
componentWillUnmount
-
componentWillUnmount()
會在組件卸載以及銷毀之前直接調用。
- 在此方法中執行必要的清理操作;
- 例如:清楚timer, 取消網絡請求或清除在
componentDidMount
中創建的訂閱等;
-
- 2.3.1.
-
2.4. 不常用的生命周期函數
- 2.4.1. 不常用的生命周期如下圖:
-
2.4.2. 不常用生命周期多出來這些方法:
getDerivedStateFromProps()、shouldComponentUpdate()、getSnapshotBeforeUpdate()
-
2.4.3.
getDerivedStateFromProps()
-
getDerivedStateFromProps()
:在更新或者第一次掛載時,如果state里面的數據需要依賴Props, 可以在這個回調函數里完成,這個函數用的非常少
-
getDerivedStateFromProps()
: state的值在任何使用時候依賴于props時候用,該方法返回一個對象來更新state
-
-
2.4.4.
shouldComponentUpdate ()
-
shouldComponentUpdate()
: 該生命周期函數很常用,等記錄到性能優化時再詳細記錄;
-
- 組件是否需要更新(shouldComponentUpdate翻譯:組件要不要更新),返回true或者false,默認是true。
-
- 當通過setState去更新數據的時候,render函數一般會直接執行,重新渲染,如果在shouldComponentUpdate生命周期里面返回false,render()不會執行,組件不會重新渲染。
-
- 某些情況下返回false,可以提高性能
-
-
2.4.5.
getSnapshotBeforeUpdate()
-
getSnapshotBeforeUpdate()
:React更新DOM之前回調的一個函數,可以獲取DOM更新前的一些信息(例如:滾動位置)
-
- (Snapshot:快照)在組件更新之前,獲取快照,保存一些數據,返回值會作為參數傳遞給componentDidUpdate()方法。
-
-
2.4.6. 更詳細的生命周期相關的內容,可以參考官網:官網
-
2.4.7. 不常用生命周期函數代碼示例如下:
- 關鍵代碼:
// 6.不常用的生命周期補充shouldComponentUpdate () {// 是否需要重新更新,默認為true,重新更新render函數,重新渲染return true}getSnapshotBeforeUpdate () {console.log('getSnapshotBeforeUpdate')return {scrollPosition: 1000}}
- 完整代碼:
import React from 'react';class HelloWorld extends React.Component {// 1.構造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}changeText () {this.setState({ message: '你好啊 李銀河' })}// 2.執行render方法render () {const { message } = this.stateconsole.log('HelloWorld render')return (<div><h2>{message}</h2><p>{message}是程序員的第一個代碼</p><button onClick={e => this.changeText()}>修改文本</button></div>)}// 3.組件被渲染到DOM:被掛載到DOM上componentDidMount () {/*** * - 1. 在掛載時,首先會創建一個組件實例,在創建實例的過程中,就會執行類中`constructor函數`(每次引入使用組件時,創建的是ReactElement, 每次調用就創建一個組件實例, 每次創建實例時,就會先執行constructor函數)* - 2. 緊接著調用類中的`render方法`* - 3. 掛載完成后會調用的`componentDidMount函數`* * * */ console.log('HelloWorld componentDidMount')}// 4.組件的DOM被更新完成:DOM發生更新componentDidUpdate (prevProps, prevState, snapshot) {/*** * - 1. 當數據發生更新時,執行setState方法* - 2. 執行setState方法后,重新渲染頁面, 重新執行render方法,它會根據最新修改之后的message,重新生成一個新的樹,然后在進行diff算法,然后決定要更新那部分* - 3. react會更新dom和refs,并調用componentDidUpdate方法* * * */ console.log('HelloWorld componentDidUpdate: ', prevProps, prevState, snapshot)}// 5.組件從DOM中卸載掉:從DOM移除掉componentWillUnmount () {console.log('HelloWorld componentWillUnmount')}// 6.不常用的生命周期補充shouldComponentUpdate () {// 是否需要重新更新,默認為true,重新更新render函數,重新渲染, 返回false, 不執行render,不重新渲染界面return true}getSnapshotBeforeUpdate () {console.log('getSnapshotBeforeUpdate')return {scrollPosition: 1000}}}export default HelloWorld;
- 2.4.1. 不常用的生命周期如下圖: