一、概述
什麼是生命週期
每個組件的實例從創建到運行再到銷燬 在這個過程中的特定階段會觸發一系列的事件
這些事件就叫做組件的生命週期函數
React的組件生命週期函數
React組件的生命週期分爲三部分:創建 運行 銷燬
組件創建
階段
(在組件的整個生命週期中只會被執行一次)
- componentWillMount / 組件將要掛載
- render / 渲染虛擬DOM元素
- componentDidMount / 組件已經掛載
組件運行
階段
(根據props屬性或state屬性的改變 有選擇性地執行0或多次
若屬性從創建開始從未改變 那就是0次
屬性改變了多少次 就執行多少次)
- componentWillReceiveProps / 組件將要接收屬性
- shouldComponentUpdate / 組件是否需要更新(判斷 返回true則渲染頁面 返回false則不渲染頁面 但數據依舊還是最新的)
- componentWillUpdate / 組件將要更新
- render / 渲染虛擬DOM元素
- componentDidUpdate / 組件完成更新
組件銷燬
階段
(在組件的整個生命週期中只會被執行一次)
- componentWillUnmount / 組件將要解除掛載
二、從案例中看生命週期
========== 組件創建階段 ==========
componentWillMount() 和 render() 和 componentDidMount()
import React from 'react';
export default class Hello extends React.Component
{
constructor(props)
{
super(props);
this.state={
msg:"aaa"
}
}
// ★【componentWillMount】在組件即將掛載到頁面之前執行 此時頁面尚未掛載到頁面中 虛擬DOM也尚未創建
componentWillMount() // 相當於Vue的created生命週期
{
// 此時無法獲取到頁面上的任何元素
console.log(document.getElementById("myh3")) // 輸出undefined
console.log(this.props.initCount) // 1
console.log(this.state.msg) // aaa
this.testFunction();
}
// 在render的return執行之前 此時虛擬DOM尚未創建 頁面上是空的 無法獲取任何元素
render()
{
console.log(document.getElementById("myh3")) // 輸出undefined
return <div>
<h1>Counter Component</h1>
<input type="button" value="+1"/>
<hr/>
<h3 id="myh3">當前數值爲{this.props.initCount}</h3>
</div>
// 在render執行完畢後 內存中就有了虛擬DOM 但頁面上依舊是尚未顯示
}
// ★【componentDidMount】在組件掛載到頁面上之後執行 此時頁面上已經有可見的DOM元素了
// 在該函數中可以放心地去操作頁面上的DOM元素了 且最早能操作DOM元素的生命週期就是這個
// 當執行完畢該函數後 即進入了運行中的狀態 該函數是創建階段的最後一個函數
componentDidMount() // 相當於Vue的mounted生命週期
{
console.log(document.getElementById("myh3")) // 能獲取到DOM 不再是undefined了
}
testFunction()
{
console.log("just test...")
}
}
========== 組件運行階段 ==========
shouldComponentUpdate 和 componentWillUpdate 和 render 和 componentDidUpdate
在這裏以一個計數器作爲案例:
import React from 'react'
import ReactDOM from 'react-dom'
import Counter from "@/components/Counter"
ReactDOM.render(<div>
<Counter initCount={1}></Counter>
</div>,document.getElementById("app"))
(爲節省篇幅 在此片段只專門列舉組件運行階段的生命週期函數)
import React from 'react';
import ReactPropTypes from "prop-types";// 提供常見的數據類型 用於類型校驗
export default class Counter extends React.Component
{
constructor(props)
{
super(props);
this.state={
count:props.initCount
}
}
render()
{
// 在組件運行階段時 調用render函數 頁面上的DOM元素還是舊的
console.log(this.refs.h3 && this.refs.h3.innerHTML)
return <div>
<input type="button" value="增加" onClick={() => {this.increaseCount()}}/>
<hr/>
<h3 id="myh3" ref="h3">當前數值爲{this.state.count}</h3>
</div>
}
increaseCount = () => {
this.setState({
count:this.state.count+1
})
}
// ★【shouldComponentUpdate】判斷組件是否需要更新
// 注:shouldComponentUpdate裏獲取的數值有遲滯性(慢半拍) 通過this獲取的數據會是前一次的
// 可以在shouldComponentUpdate的入參中定義nextProps和nextState以獲取props和state裏的實時數據(入參的名稱可任意取)
shouldComponentUpdate(nextProps,nextState)
{
// shouldComponentUpdate要求必須返回一個布爾值 作爲組件更新生命週期是否繼續執行的參考
// 若返回【true】 則繼續執行後面的生命週期
// 若返回【false】 則不執行後面的生命週期了 若點擊增加按鈕 state中的值依舊會增加 但是後面的render函數並不會被調用 因此虛擬DOM和頁面上的數據都是舊的
return true;
}
// ★【componentWillUpdate】在組件將要更新時執行 將要更新的意思就是已經確定要更新 但是還沒有實際的更新操作
// 在進入該生命週期函數的時候 內存中的虛擬DOM和頁面上的DOM元素都還是舊的
componentWillUpdate()
{
// 此時頁面上的DOM節點還是舊的 尚未更新
console.log(this.refs.h3.innerHTML) // 比如頁面上展示的數值爲3 那麼此時這裏打印的還是2
}
// ★【componentDidUpdate】在組件完成更新後執行 此時state中的數據和虛擬DOM和頁面上的DOM都已更新爲最新的了
componentDidUpdate()
{
console.log(this.refs.h3.innerHTML)
}
}
componentWillReceiveProps
這裏以一個父子組件作爲案例:
import React from 'react';
// 父組件
export default class Parent extends React.Component
{
constructor(props)
{
super(props);
this.state={
msg:"Old Parent Msg"
}
}
render()
{
return <div>
<h1>Parent</h1>
<input type="button" value="修改" onClick={() => {this.changeMsg()}}/>
<hr/>
<Son parentMsg={this.state.msg}></Son>
</div>
}
changeMsg = () => {
this.setState({
msg:"New Parent Msg"
})
}
}
// 子組件
class Son extends React.Component
{
constructor(props)
{
super(props);
this.state={}
}
render()
{
return <div>
<h3>Son : {this.props.parentMsg}</h3>
</div>
}
// ★【componentWillReceiveProps】在組件將要接收外界傳來的props時執行
// 當組件第一次被渲染到頁面上的時候並不會觸發該生命週期函數
// 只有 外界通過某些事件重新修改了傳入的props數據 纔會觸發該生命週期函數
componentWillReceiveProps(nextProps)
{
// 通過this.props獲得的並不是最新的props數據 而是被觸發後上一次的舊值
// 若要獲取最新的屬性值 則需要在入參中定義nextProps 然後通過nextProps獲取
console.log("舊值:"+this.props.parentMsg+" - 新值:"+nextProps.parentMsg)
}
}
(組件銷燬階段不太方便演示 在此就不演示了)
生命週期函數的入參列表:
- Mounting創建:
- constructor()
- componentWillMount()
- render()
- componentDidMount()
- Updating更新:
- componentWillReceiveProps(nextProps) // 獲取最新props
- shouldComponentUpdate(nextProps, nextState) // 獲取最新屬性
- componentWillUpdate(nextProps, nextState) // 獲取最新屬性
- render()
- componentDidUpdate(prevProps, prevState) // 在全部更新完畢後 可以獲取舊的屬性(prev)
- Unmounting銷燬:
- componentWillUnmount()
三、從示意圖中看生命週期
(畫了張示意圖 希望能夠幫助理解)