本文解讀了react生命周期的源碼,如果你還是個入門的小白,當然可以忽略源碼,看一看作者寫的demo。也可以明白生命周期奧義。
React組件生命周期根據廣義的定義,可分為掛在、渲染和卸載幾個階段。當渲染后的組件再次更新時,react會重新去渲染組件,直至卸載。
在定義組建的時候,我們會根據需要在組件在生命周期不同階段實現不同邏輯。
當組件首次掛載時,按順序執行getDefaultProps、getInitialState、componentWillMount、render、componentDidMount。
當卸載組件時,執行componentWillUnmount
當重新掛載組件時候,此時按順序執行 getInitialState、componentWillMount、 render、componentDidMount(此時不會執行getDefaultProps)
當組件重新渲染時,組件接收到更新狀態,此時執行順序是:componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate
在es6的與法理static defaultProps = {} 相當于其實就是調用內部的getDefaultProps,this.state = {} 就相當于getInitialState。
自定義組件的生命周期主要通過三個階段:
MOUNTING、RECEIVE_PROPS、UNMOUNTING對應的方法分別為:mountComponent、updateComponent、unmountComponent
一、使用createClass創建自定義組件
createClass方法是創建自定義組件的入口方法。該方法只在生命周期中調用一次,所有的實例化的props都會共享。
找到ReactClass.js
以下為源碼
var ReactClass = {
createClass: function (spec) {
var Constructor = function (props, context, updater) {
// 觸發自動綁定
if (this.__reactAutoBindPairs.length) {
bindAutoBindMethods(this);
}
// 初始化參數
this.props = props;
this.context = context;
this.refs = emptyObject;// 本組件對象的引用,可以利用它來調用組件的方法
this.updater = updater || ReactNoopUpdateQueue;
// 調用getInitialState()來初始化state變量
this.state = null;
var initialState = this.getInitialState ? this.getInitialState() : null;
if (initialState === undefined && this.getInitialState._isMockFunction) {
initialState = null;
}
this.state = initialState;
};
// 繼承父類
Constructor.prototype = new ReactClassComponent();
Constructor.prototype.constructor = Constructor;
Constructor.prototype.__reactAutoBindPairs = [];
//按順序合并mixins
injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));
mixSpecIntoComponent(Constructor, spec);
// 待到所有mixins合并完成,調用getDefaultProps,并掛載到組件類上(整個周期只會調用一次)。
if (Constructor.getDefaultProps) {
Constructor.defaultProps = Constructor.getDefaultProps();
}
if (Constructor.getDefaultProps) {
Constructor.getDefaultProps.isReactClassApproved = {};
}
if (Constructor.prototype.getInitialState) {
Constructor.prototype.getInitialState.isReactClassApproved = {};
}
!Constructor.prototype.render ? process.env.NODE_ENV !== 'production' ? invariant(false, 'createClass(...): Class specification must implement a `render` method.') : _prodInvariant('83') : void 0;
// 減少查找,并設置原型時間
// React中暴露給應用調用的方法,如render componentWillMount。
// 如果應用未設置,則將他們設為null
for (var methodName in ReactClassInterface) {
if (!Constructor.prototype[methodName]) {
Constructor.prototype[methodName] = null;
}
}
return Constructor;
},
injection: {
injectMixin: function (mixin) {
injectedMixins.push(mixin);
}
}
};
createClass主要做了:
定義構造方法Constructor,構造方法中進行props,refs等的初始化,并調用getInitialState來初始化state
調用getDefaultProps,并放在defaultProps類變量上。這個變量不屬于某個單獨的對象??衫斫鉃閟tatic 變量
將React中暴露給應用,但應用中沒有設置的方法,設置為null。
二、MOUNTING
MOUNTING階段定義的方法是mountCompont,如ReactCompositeComponent.js下
所示
//初始化組件,注冊事件監聽
mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
//當前元素對應的上下文
this._context = context;
this._mountOrder = nextMountID++;
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
// 添加到ReactComponentElement元素的props屬性
var publicProps = this._currentElement.props;
// 通過Component.contextTypes過濾由上層組件注入的context屬性,并做校驗
var publicContext = this._processContext(context);
// 純函數無狀態組件、或者繼承自PureComponent的純組件構造函數、或者繼承自Component的組件構造函數
var Component = this._currentElement.type;
// 傳入組件ReactComponent的第三個參數updater,默認是ReactUpdateQueue模塊,用于實現setState等方法
var updateQueue = transaction.getUpdateQueue();
// 校驗是否純組件或組件;返回否值,當作非狀態組件、或ReactClass的工廠函數處理
var doConstruct = shouldConstruct(Component);
// 創建純組件或組件實例,或者獲取無狀態組件的返回值
var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
// 待掛載的ReactComponentElement元素
var renderedElement;
// 無狀態組件,沒有更新隊列它只專注于渲染
if (!doConstruct && (inst == null || inst.render == null)) {
renderedElement = inst;
warnIfInvalidElement(Component, renderedElement);
// 將無狀態組件function(props,context,updateQueue){}包裝為帶有render原型方法的構造函數形式
inst = new StatelessComponent(Component);
this._compositeType = CompositeTypes.StatelessFunctional;
} else {
if (isPureComponent(Component)) {
// 添加純組件標識
this._compositeType = CompositeTypes.PureClass;
} else {
// 添加組件標識
this._compositeType = CompositeTypes.ImpureClass;
}
}
// 原本作為構造函數的參數傳入,為方便起見,再次賦值,同時保證實例數據的準確性
inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;
inst.updater = updateQueue;
// ReactInstanceMap中添加組件實例
this._instance = inst;
ReactInstanceMap.set(inst, this);
// 獲取初始state,并提示state只能設置為對象形式
var initialState = inst.state;
if (initialState === undefined) {
inst.state = initialState = null;
}
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
// 執行實例inst的render方法,嵌套調用mountComponent,將返回值ReactNode元素轉化成DomLazyTree輸出
var markup;
if (inst.unstable_handleError) {
markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
} else {
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
}
// 向后置鉤子transaction.getReactMountReady()中添加實例的生命周期方法componentDidMount
if (inst.componentDidMount) {
if (process.env.NODE_ENV !== 'production') {
transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this);
} else {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}
}
return markup;
}
performInitialMountWithErrorHandling: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
var markup;
var checkpoint = transaction.checkpoint();
try {
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onError();
}
}
transaction.rollback(checkpoint);
this._instance.unstable_handleError(e);
if (this._pendingStateQueue) {
// _processPendingState方法獲取組件setState、replaceState方法執行后的最終state
this._instance.state = this._processPendingState(this._instance.props, this._instance.context);
}
checkpoint = transaction.checkpoint();
this._renderedComponent.unmountComponent(true);
transaction.rollback(checkpoint);
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
}
return markup;
},
performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
var inst = this._instance;
// 執行組件實例的componentWillMount方法
// componentWillMount方法內調用setState、replaceState,_pendingStateQueue有值,刷新state后再行繪制
inst.componentWillMount();
if (this._pendingStateQueue) {
// _processPendingState方法獲取組件setState、replaceState方法執行后的最終stat
inst.state = this._processPendingState(inst.props, inst.context);
}
}
// 間接執行ReactClass或TopLevelWrapper實例的render方法,獲取待掛載的元素ReactNode
// 組件若為函數式無狀態組件function(props,context,updateQueue){},renderedElement由傳參提供
if (renderedElement === undefined) {
// 調用組件實例inst的render方法,獲取待掛載的元素ReactNode
renderedElement = this._renderValidatedComponent();
}
var nodeType = ReactNodeTypes.getType(renderedElement);
this._renderedNodeType = nodeType;
var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
);
// render方法內子組件實例
this._renderedComponent = child;
var selfDebugID = 0;
// 嵌套調用mountComponent,完成renderedElement元素相應組件的實例化及render方法執行
// 最終通過ReactDomElement轉化為DOMLazyTree對象輸出,其node屬性為需要插入文檔dom對象
var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), selfDebugID);
return markup;
}
在組件掛載的過程中,propTypes和defaultProps分別代表props類型檢查和默認類型。之后我們會看到兩個方法componentWillMount和componentDidMount,其中,componentWillMount在render之前執行,componentDidMount在render之后執行分別代表了渲染前后的時刻。如果我們在componentWillMount執行setState方法,組件會更新state,但是組件只渲染一次。因此這是無意義的,初始化state可以放在this.state里。
其實,mountComponent本質上是通過遞歸渲染內容的,父組件的componentWillMount在子組件的componentWillMount之前調用,componentDidMount在子組件的componentDidMount之后調用。
看下面一組代碼:
class Botton extends React.Component{
constructor(props){
super(props)
this.state = {
value:false,
pro : 'aaa'
}
}
componentWillMount(){
console.log('子組件willmount')
}
componentDidMount(){
console.log('子組件didmount')
this.setState({pro:'bbb'})
}
setSta(){
this.setState({
value: true,
})
}
set(){
this.setState({
pro: '1111',
})
}
render(){
console.log("子組件render")
let comp ,style;
const { value } = this.state;
return(
<div>
<button onClick={ this.setSta.bind(this) }>切換</button>
<button onClick={ this.set.bind(this) }>切換2</button>
</div>
)
}
}
class Wrap extends React.Component {
constructor(props){
super(props)
this.state = {
value : false
}
}
componentWillMount(){
console.log('父組件willmount')
}
componentDidMount(){
console.log('父組件didmount')
}
sets(){
this.setState({
value : true
})
}
render(){
console.log("父組件render")
let style;
if(this.state.value){
style = {
display : 'block'
}
}else{
style = {
display : 'none'
}
}
return(
<div>
<Botton style={style}/>
<button onClick={this.sets.bind(this)}>切換</button>
</div>
)
}
}
const handleChange = (value) => {
console.log(value)
}
ReactDOM.render(
<div>
<Wrap />
</div>,
document.getElementById('app')
)
運行結果:
首先父組件wrap 初始化 props state,之后執行wrap的componentWillMount方法,合并state,執行render方法,在render方法里調用Botton組件,之后執行Botton的componentWillMount -- render --componentDidMount 然后執行父組件componentDidMount。
三、RECEIVE_PROPS階段
還是先看源碼:
// 接受新的組件待渲染元素nextElement,以替換舊的組件元素this._currentElement
// 通過performUpdateIfNecessary方法調用,nextElement由this._pendingElement提供
// 該方法觸發執行的實際情形是ReactDom.render(ReactNode,pNode)掛載的組件元素,其父節點pNode由react方式繪制
// 通過_updateRenderedComponent方法調用,nextElement為待變更的子組件元素
receiveComponent: function (nextElement, transaction, nextContext) {
var prevElement = this._currentElement;
var prevContext = this._context;
this._pendingElement = null;
this.updateComponent(transaction, prevElement, nextElement, prevContext, nextContext);
},
// 判斷props變更情況,執行shouldComponentUpdate方法,重繪組件或者更改組件的屬性
// 參數transaction,組件重繪時用于向子組件提供updater參數,setState等方法可用;以及實現componentWillMount掛載功能
// 參數prevParentElement變更前的組件元素ReactNode,nextParentElement變更后的組件元素,作為render方法渲染節點的父元素
// 參數prevUnmaskedContext更迭前的context,nextUnmaskedContext更迭后的context
updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {
var inst = this._instance;
var willReceive = false;
var nextContext;
// 更新context
if (this._context === nextUnmaskedContext) {
nextContext = inst.context;
} else {
nextContext = this._processContext(nextUnmaskedContext);
willReceive = true;
}
var prevProps = prevParentElement.props;
var nextProps = nextParentElement.props;
// 包含僅待渲染元素的props變更
if (prevParentElement !== nextParentElement) {
willReceive = true;
}
// 更新context、或變更帶渲染組件元素或其props時willReceive賦值為真,由父組件發起,調用componentWillReceiveProps方法
if (willReceive && inst.componentWillReceiveProps) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillReceiveProps');
}
}
inst.componentWillReceiveProps(nextProps, nextContext);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillReceiveProps');
}
}
}
//將最新的state合并到隊列中
var nextState = this._processPendingState(nextProps, nextContext);
//更新組件
var shouldUpdate = true;
// 調用組件的shouldComponentUpdate判斷是否需要重繪
// 純組件不能設置shouldComponentUpdate方法,僅判斷props、state是否變更
if (!this._pendingForceUpdate) {
if (inst.shouldComponentUpdate) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'shouldComponentUpdate');
}
}
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'shouldComponentUpdate');
}
}
} else {
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
}
}
}
this._updateBatchNumber = null;
if (shouldUpdate) {
//重新更新隊列
this._pendingForceUpdate = false;
// 執行componentWillUpdate方法,重繪組件實例render方法內待渲染的子組件,掛載componentDidUpdate方法
this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);
} else {
// 只變更組件的部分屬性,不開啟重繪功能
this._currentElement = nextParentElement;
this._context = nextUnmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
}
},
// 執行componentWillUpdate方法,重繪組件實例render方法內待渲染的子組件,掛載componentDidUpdate方法
_performComponentUpdate: function (nextElement, nextProps, nextState, nextContext, transaction, unmaskedContext) {
var inst = this._instance;
var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
var prevProps;
var prevState;
var prevContext;
// 如果存在componentDidUpdate就將props、state、context保存一份
if (hasComponentDidUpdate) {
prevProps = inst.props;
prevState = inst.state;
prevContext = inst.context;
}
// 執行componentWillUpdate方法
if (inst.componentWillUpdate) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillUpdate');
}
}
inst.componentWillUpdate(nextProps, nextState, nextContext);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillUpdate');
}
}
}
this._currentElement = nextElement;
this._context = unmaskedContext;
//更新props、state、context
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
// 以更新子組件的方式或重新創建子組件的方式重繪render方法待渲染的子組件
this._updateRenderedComponent(transaction, unmaskedContext);
// 向后置鉤子transaction.getReactMountReady()中添加實例的生命周期方法componentDidUpdate
if (hasComponentDidUpdate) {
if (process.env.NODE_ENV !== 'production') {
transaction.getReactMountReady().enqueue(invokeComponentDidUpdateWithTimer.bind(this, prevProps, prevState, prevContext), this);
} else {
transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
}
}
},
// 以更新子組件的方式或重新創建子組件的方式重繪render方法待渲染的子組件
_updateRenderedComponent: function (transaction, context) {
// 組件render待渲染的子組件實例
var prevComponentInstance = this._renderedComponent;
var prevRenderedElement = prevComponentInstance._currentElement;
// _renderValidatedComponent方法調用組件實例inst的render方法,獲取待掛載的元素
var nextRenderedElement = this._renderValidatedComponent();
// shouldUpdateReactComponent方法返回真值,更新組件實例;返回否值,銷毀實例后、重新創建實例
// 組件元素的構造函數或key值不同,銷毀實例后再行創建
// render方法子組件構造函數及key相同,通過ReactReconciler.receiveComponent方法更新子組件實例
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
ReactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context));
} else {
//渲染組件
var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
ReactReconciler.unmountComponent(prevComponentInstance, false);
var nodeType = ReactNodeTypes.getType(nextRenderedElement);
this._renderedNodeType = nodeType;
var nextMarkup = ReactReconciler.mountComponent(child, transaction, this._hostParent, this._hostContainerInfo, this._processChildContext(context), selfDebugID);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onSetChildren(this._debugID, child._debugID !== 0 ? [child._debugID] : []);
}
}
// 替換文檔中掛載的Dom元素DomLazyTree
this._replaceNodeWithMarkup(oldHostNode, nextMarkup, prevComponentInstance);
}
},
updateComponent方法:負責判斷props變更情況,調用componentWillReceiveProps方法,無論屬性是否變化,只要父組件發生render的時候子組件就會調用componentWillReceiveProps。調用組件的shouldComponentUpdate判斷是否需要更新。
_performComponentUpdate方法:負責執行componentWillUpdate方法,重繪組件實例render方法內待渲染的子組件,掛載componentDidUpdate方法。
_updateRenderedComponent方法:更新子組件的方式或重新創建子組件的方式重繪render方法待渲染的子組件
upadteComponent負責管理生命周期的componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate。
通過下面例子具體看一下:
class Botton extends React.Component{
constructor(props){
super(props)
this.state = {
value:false,
pro : 'aaa'
}
}
componentWillMount(){
console.log('子組件willmount')
}
componentDidMount(){
console.log('子組件didmount')
this.setState({pro:'bbb'})
}
componentWillUpdate(){
console.log('子組件WillUpdata')
}
componentDidUpdate(){
console.log('子組件DidUpdata')
}
componentWillReceiveProps(nextProps) {
console.log('子組件WillReceiveProps')
}
setSta(){
this.setState({
value: true,
})
}
set(){
this.setState({
pro: '1111',
})
}
render(){
console.log("子組件render")
let comp ,style;
const { value } = this.state;
if(value){
comp = (
<div >
你好
<p>123</p>
</div>
)
}else{
comp = (
<div >
ta好
</div>
)
}
return(
<div>
{comp}
<button onClick={ this.setSta.bind(this) }>切換</button>
<button onClick={ this.set.bind(this) }>切換2</button>
</div>
)
}
}
class Wrap extends React.Component {
constructor(props){
super(props)
this.state = {
value : false
}
}
componentWillMount(){
console.log('父組件willmount')
}
componentDidMount(){
console.log('父組件didmount')
this.setState({pro:'bbb'})
}
componentWillUpdate(){
console.log('父組件WillUpdata')
}
componentDidUpdate(){
console.log('父組件DidUpdata')
}
sets(){
this.setState({
value : true
})
}
render(){
console.log("父組件render")
let style;
if(this.state.value){
style = {
display : 'block'
}
}else{
style = {
display : 'none'
}
}
return(
<div>
<Botton style={style }/>
<button onClick={this.sets.bind(this)}>切換</button>
</div>
)
}
}
ReactDOM.render(
<div>
<Wrap />
</div>,
document.getElementById('app')
)
運行結果:
當我們點擊父級切換按鈕的時候調用sets方法,從而執行setState方法,于是父級組件開始進入RECEIVE_PROPS階段。
從執行結果上看更新過程也是遞歸執行的。
shouldComponentUpdate:
組件掛載之后,每次調用setState后都會調用shouldComponentUpdate判斷是否需要重新渲染組件。默認返回true,需要重新render。在比較復雜的應用里,有一些數據的改變并不影響界面展示,可以在這里做判斷,優化渲染效率。
//在botton里添加
shouldComponentUpdate(){
return false;
}
結果
四、UNMOUNTING
生命周期的最后一個階段componentWillUnmount代表將要卸載。
在componentWillUnmount內所有的狀態都被置為null
if (this._renderedComponent) {
ReactReconciler.unmountComponent(this._renderedComponent, safely);
this._renderedNodeType = null;
this._renderedComponent = null;
this._instance = null;
}
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
this._pendingCallbacks = null;
this._pendingElement = null;
this._context = null;
this._rootNodeID = 0;
this._topLevelWrapper = null;
//清除公公類
ReactInstanceMap.remove(inst);
看完生命周期,我想大家應該對props、state有了新的認識。大家可以深入去讀一些源碼,了解react的生命周期,在編程上會給大家更多的幫助。
我們試著分析一下上面的那個demo:
1.最外層組件wrap我們并沒有設置props所以它的props是空的,
2.this.state即getInitialState 設置了一個value = false
3.執行自己的componentWillMount方法
4.合并state
5.執行render方法,
<Botton style={{display:'none'}}/>
我們在此給 Botton設置了 style 屬性
6.執行getDefaultProps方法設置style屬性
7.執行getInitialState方法即this.state = {
value:false,
pro : 'aaa'
}
8.執行componentWillMount方法
9.合并state
10.執行render方法
11.執行componentDidMount方法
12.執行父級Wrap的componentDidMount方法,此時我們設置
setState({
value : true
})
13.Wrap會執行componentWillUpdate方法(shouldComponentUpdate默認為更新)
14.創建component實例
15.render
16.子組件調用componentWillReceiveProps
17.shouldComponentUpdate默認為更新
18.調用componentWillUpdate
19.創建component實例 render
20.componentDidUpdate
21.Wrap組件componentDidUpdate
當點擊 切換按鈕時
繼續13-21.
有錯歡迎指正。
下一篇我們深入挖掘 setState 函數。
你也可以看其他文章
React基礎2--深入挖掘setState
React基礎3--diff算法