React 組件的一生海渊,是光榮的一生,是革命的一生,在它的一生中會經(jīng)歷這樣幾個階段:
- 裝載階段
- 更新階段
- 銷毀階段
每一階段都會觸發(fā)相應(yīng)的生命周期函數(shù)贮乳,下面依次來說一說這些生命周期函數(shù)。
裝載階段
裝載階段就是組件第一次被渲染時的階段恬惯,這一階段相關(guān)的生命周期函數(shù)有:
- constructor
- componentWillMount
- render
- componentDidMount
下面是這些方法的一些說明:
constructor:用來初始化組件向拆,獲取組件的初始屬性(props)或狀態(tài)(state),其作用類似于 ES5 createClass 寫法中的 getInitialState 和 getDefaultProps 函數(shù)酪耳。
componentWillMount:在組件即將裝載前調(diào)用浓恳。
render:用來生成一個 JSX 描述結(jié)構(gòu)刹缝,告訴 React 本次要裝載哪些東西,需要注意的是颈将,render方法只用來返回一個 JSX 結(jié)構(gòu)梢夯,并不負責裝載,具體的裝載工作由 React 完成晴圾。
componentDidMount:在組件被裝載之后調(diào)用颂砸,此時已經(jīng)完成了 DOM 更新,組件已經(jīng)被裝載到真實的 DOM 樹中了死姚,可以進行相應(yīng)的 DOM 操作人乓,比如添加原生的 DOM 事件就應(yīng)該放在 componentDidMount 中進行。
看一個例子:
import React,{ Component } from "react";
import "./App.css";
export default class App extends Component{
constructor(props){
super(props);
console.log("constructor")
}
componentWillMount() {
console.log("componentWillMount")
}
componentDidMount(){
console.log("componentDidMount")
}
render(){
console.log("render")
return(
<div className = "App-div">
</div>
);
}
}
打開瀏覽器都毒,控制臺中輸出的結(jié)果為:
constructor
componentWillMount
render
componentDidMount
多個組件的情況
如果 App 組件中渲染了其他的組件色罚,那么這些組件的生命周期函數(shù)是怎樣調(diào)用的呢?
新建一個 SubComponent.js:
import React,{ Component } from "react";
export default class Sub extends Component{
constructor(props){
super(props);
console.log("constructor")
}
componentWillMount() {
console.log("componentWillMount")
}
componentDidMount(){
console.log("componentDidMount")
}
render(){
console.log("render")
return(
<div>我是子組件</div>
);
}
}
修改 App.js温鸽,讓其渲染兩個子組件:
import React,{ Component } from "react";
import Sub from "./SubComponent.js";
import "./App.css";
export default class App extends Component{
render(){
const SubArr = [1,2].map((v)=>{
return <Sub key = {v} />
});
return(
<div className = "App-div">
{ SubArr }
</div>
);
}
}
打開瀏覽器保屯,看下控制臺中的輸出:
constructor
componentWillMount
render
constructor
componentWillMount
render
componentDidMount
componentDidMount
這兩個子組件的 componentDidMount 函數(shù)并不是在 render 函數(shù)之后執(zhí)行,而是放在最后執(zhí)行了涤垫,為什么呢姑尺?
還是回到上面那句話:render 函數(shù)只用來生成 JSX 組件結(jié)構(gòu),不負責裝載工作蝠猬,具體的狀態(tài)工作交個 React 完成切蟋。
React 需要在形成完整的 JSX 結(jié)構(gòu)之后才進行裝載,也就是說榆芦,要在兩個 render 函數(shù)都調(diào)用完成后才進行裝載工作柄粹,而后再調(diào)用 componentDidMount 生命周期函數(shù)。
更新階段
如果組件中的 state 或者 props 發(fā)生了改變匆绣,React 就會更新該組件驻右,此時會調(diào)用更新階段中相關(guān)的生命周期函數(shù):
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
componentWillReceiveProps 函數(shù)在傳遞給子組件的 props 變化或者父組件更新時調(diào)用。
修改 SubComponent.js:
import React,{ Component } from "react";
export default class Sub extends Component{
componentWillReceiveProps(nextProps) {
console.log("componentWillReceiveProps");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
return true;
}
componentDidUpdate(prevProps, prevState) {
console.log("componentDidUpdate");
}
render(){
console.log("render")
return(
<div>我是子組件</div>
);
}
}
修改 App.js崎淳,增加一個按鈕堪夭,點擊時強制更新:
import React,{ Component } from "react";
import Sub from "./SubComponent.js";
import "./App.css";
export default class App extends Component{
render(){
const SubArr = [1,2].map((v)=>{
return <Sub key = {v} />
});
return(
<div className = "App-div">
<button onClick = { ()=>this.forceUpdate() }>點擊強制更新</button>
{ SubArr }
</div>
);
}
}
點擊更新按鈕,看下控制臺的輸出:
componentWillReceiveProps
shouldComponentUpdate
render
componentWillReceiveProps
shouldComponentUpdate
render
componentDidUpdate
componentDidUpdate
可見拣凹,父組件的更新也會引起子組件的更新森爽,如果父組件中有很多子組件,默認情況下嚣镜,在父組件進行更新(通常是父組件的 props 或 state 發(fā)生變化)或者父組件傳遞給子組件的 props 發(fā)生變化時爬迟,會引起所有子組件的更新。如果子組件數(shù)量眾多菊匿,功能復(fù)雜付呕,那么進行更新會產(chǎn)生大量的性能浪費计福,即使是在使用 Virtual DOM 的情況下。針對這種情況徽职,我們就需要使用下面介紹的 shouldComponentUpdate 函數(shù)了棒搜。
P.S.在使用 setState 時并不會調(diào)用 componentWillReceiveProps,而是直接調(diào)用 shouldComponentUpdate 函數(shù)活箕。
shouldComponentUpdate 函數(shù)接受兩個參數(shù):nextProps 和 nextState,分別表示引發(fā)本次組件更新的 props 或 state可款,該函數(shù)的返回值的真假決定組件是否更新育韩,默認返回 true。實際應(yīng)用場景中闺鲸,我們可以將這兩個參數(shù)和組件當前的 props(this.props)和當前的 state(this.state)進行比對筋讨,以決定是否更新組件,提高性能摸恍。
為了方便演示悉罕,給每個子組件傳遞一個名為 flag 的 prop:
import React,{ Component } from "react";
import Sub from "./SubComponent.js";
import "./App.css";
export default class App extends Component{
render(){
const SubArr = [1,2].map((v)=>{
return <Sub key = {v} flag = "1" />
});
return(
<div className = "App-div">
<button onClick = { ()=>this.forceUpdate() }>點擊強制更新</button>
{ SubArr }
</div>
);
}
}
修改 SubComponent.js:
import React,{ Component } from "react";
export default class Sub extends Component{
componentWillReceiveProps(nextProps) {
console.log("componentWillReceiveProps");
}
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.flag !== this.props.flag);
}
componentDidUpdate(prevProps, prevState) {
console.log("componentDidUpdate");
}
render(){
console.log("render")
return(
<div>我是子組件</div>
);
}
}
控制臺的輸出結(jié)果為:
componentWillReceiveProps
componentWillReceiveProps
父組件更新后,會引起子組件的更新立镶,首先就是調(diào)用 componentWillReceiveProps 方法壁袄,之后在 shouldComponentUpdate 中進行屬性值的判斷,由于兩次渲染都傳遞了相同的屬性值媚媒,因此沒有必要更新組件嗜逻,shouldComponentUpdate 函數(shù)返回 false,后面的一系列生命周期函數(shù)就不執(zhí)行了缭召,提高了渲染性能栈顷。
componentWillUpdate 函數(shù)在組件即將更新時調(diào)用。
render 函數(shù)用來產(chǎn)出本次更新的 JSX 組件樹結(jié)構(gòu)嵌巷。
componentDidUpdate 函數(shù)在組件完成更新后調(diào)用萄凤。當組件被更新時,會重新繪制 DOM 樹搪哪。同 componentDidMount 函數(shù)靡努,該函數(shù)并不是在每個子組件 render 方法執(zhí)行后立即被執(zhí)行的,而是等待 DOM 樹更新后再執(zhí)行噩死。
卸載過程
卸載過程就一個函數(shù):
- componentWillUnmount
該函數(shù)用來做一些清理工作颤难,比如如果在組件中使用了原生的 DOM 事件,需要在組件被銷毀前移除該事件已维,防止內(nèi)存泄露行嗤,或者清理定時器等。
完垛耳。