1. 程式人生 > >R4-React生命週期詳解及最新變動

R4-React生命週期詳解及最新變動

這一章來介紹react元件的生命週期。之前介紹過純函式元件是沒有生命週期的,那到底生命週期是什麼?其實簡單來講就是元件的初始和消亡,就如同小草的生長一樣(配圖隨機,純屬護眼)從發芽到消亡。元件在這個過程中會經歷那些階段,又是如何標誌這些階段的。
生命初始
本章的重點就是要搞明白的就是下面的重點以及思維導圖(前三點必須掌握):
1.元件生命週期有哪幾個階段?
2.每個階段又包含什麼?
3.每個階段周期函式的呼叫順序什麼?
*4.根據原始碼探究為什麼是這樣的順序?(感興趣的可以看看)
元件生命週期

1.元件掛載階段

元件掛載是指元件建立例項時所必須經歷的一個過程,其中有三個函式是這一階段必須執行的。如思維導圖所示,分別是
componentWillMount():元件渲染之前執行的函式

,且元件例項建立之後不再執行,即只執行一次
render():渲染元件,該函式同樣屬於更新階段,負責渲染,無論是建立還是更新都需要重新渲染,就需要這個函式。
componentDidMount():元件渲染之後執行,且僅執行一次。想拿到元件例項只能在該函式中以及執行之後才可以。

2.元件更新階段

導致元件更新有兩種情況,父元件props發生變化,元件state值發生變化。元件更新階段稍微經歷的函式多一點,而且都帶了引數的,一定注意這些引數,寫元件的時候有大用。
componentWillReceiveProps(nextProps):該函式只在父元件創給子元件的屬性值,即子元件需要的props值發生變化時才會觸發執行。引數nextProps則是已經改變了的props值。


shouldComponentUpdate(nextProps,nextState): 控制是否要更新元件,他必須返回一個布林值,false不更新,true更新。不更新時則不再執行更新階段下面的函式。所以,引數nextProps和nextState都是已經改變的值,可根據他們判斷是否更新元件,由你自己掌握。
componentWillUpdate(nextProps,nextState): 更新元件渲染前執行,引數與上一個函式引數一致。
render(): 渲染更新的元件
componentDidUpdate(preProps,preState): 元件更新後即重新渲染後執行,注意引數,它是props和state變化前的值
。元件中如果有新出現的DOM結構,也只能在這個函式執行之後才能拿到例項。

3.解除安裝階段

解除安裝就比較簡單了,只有一個函式。componentWillUnmount.

生命週期是不是很簡單,就這幾個函式,記住執行順序,以及觸發條件,幹了什麼。你就已經基本掌握了生命週期了。生命週期的程式碼這裡就不貼了,去文章後面找到Github地址,下載原始碼,既可以看到例項,也可以跑出來看效果。

4.React生命週期的變動

很尷尬,之前做筆記的時候看的原始碼現在已經不合時宜了,react從16版本開始有了比較大的改動,剛去看一下官網最新的已經是16.5.2。原始碼部分非常多,這裡不再貼原始碼。我僅提煉最重要的進行說明。

第一點,生命週期中的多了兩個函式:static getDerivedStateFromProps,getSnapshotBeforeUpdate。

第二點,生命週期將在react17中徹底取消componentWillMount、componentWillReceiveProps和componentWillUpdate三個生命週期函式。17版本之前原生命週期依然保留,並添加了三個對應的帶有UNSAFE_字首的三個周期函式;且如果同時定義了getDerivedStateFromProps,則只會執行getDerivedStateFromProps。

mountClassInstance中的程式碼片段,元件掛載時,前兩個if正是用getDerivedStateFromProps替換掉了掛載時的componentWillMount

    // 元件掛載時先判斷是否定義這個靜態函式,如果定義了,則不再執行componentWillMount方法
    var getDerivedStateFromProps = workInProgress.type.getDerivedStateFromProps;
    if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(workInProgress, getDerivedStateFromProps, props);// 執行這個getDerivedStateFromProps方法
    instance.state = workInProgress.memoizedState;
    }
    // ctor = workInProgress.type;如果定義了getDerivedStateFromProps則不再執行裡面的函式。
    if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
    // 如果沒有定義getDeriveStateFromProps則執行此方法,此方法會判斷是否定義了componentWillMount方法,如果定義了則會執行
    callComponentWillMount(workInProgress, instance);
    updateQueue = workInProgress.updateQueue;
    if (updateQueue !== null) {
      processUpdateQueue(workInProgress, updateQueue, props, instance, renderExpirationTime);
      instance.state = workInProgress.memoizedState;
    }
    }

updateClassInstance中的程式碼片段,元件state和props變化引起元件更新如何替換掉了componentWillReceiveProps函式

    var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
    // 是否定義了新的生命週期函式,如果定義了則在callComponentWillReceiveProps,該方法內會判斷是否定義了UNSAFE_字首的以及不加字首的componentWillReceiveProps方法,並執行。
    var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
    // 定義了新的生命週期函式則不再執行callComponentWillReceiveProps
    if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
    if (oldProps !== newProps || oldContext !== newContext) {
      callComponentWillReceiveProps(workInProgress, instance, newProps, newContext);
    }
    }

同樣在updateClassInstance中,按照順序,componentWillReceiveProps執行之後是shouldComponentUpdate,並且傳入新的state和props。這裡也是一樣的,沒有變化。


   	 // 判斷是否定義了新函式,定義了則去執行
   	 if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(workInProgress, getDerivedStateFromProps, newProps);
    newState = workInProgress.memoizedState;
    }
    // 判斷是否更新元件,checkShouldComponentUpdate方法原始碼在下面
    var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext);

    if (shouldUpdate) {// 根據是否要更新來決定是否執行下面的程式碼
    // In order to support react-lifecycles-compat polyfilled components,
    // Unsafe lifecycles should not be invoked for components using the new APIs.
    // hasNewLifecycles就是上一個程式碼片段中的變數,元件中如果用新的生命週期函式則為true
    if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function')) {
      startPhaseTimer(workInProgress, 'componentWillUpdate');
      if (typeof instance.componentWillUpdate === 'function') {
        instance.componentWillUpdate(newProps, newState, newContext);// instance當前元件的例項
      }
      if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
        instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
      }
      stopPhaseTimer();
    }
    // 這裡跟之前的版本很不一樣,之前是會先經過render,然後是在這裡立即判斷是否定義了componentDidUpdate函式,然後立即執行
    if (typeof instance.componentDidUpdate === 'function') {
      workInProgress.effectTag |= Update;// 現在則是通過位運算先標記,render之後在執行。
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      workInProgress.effectTag |= Snapshot;
    }
    } else {....}

checkShouldComponentUpdate原始碼:

    function checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext) {
    var instance = workInProgress.stateNode;
    var ctor = workInProgress.type;
    if (typeof instance.shouldComponentUpdate === 'function') {// 定義了則執行
    startPhaseTimer(workInProgress, 'shouldComponentUpdate');
    // 並把返回值付給shouldUpdate,依次作為返回值
    var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, newContext);
    stopPhaseTimer();

    {
      !(shouldUpdate !== undefined) ? warning(false, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentName(workInProgress) || 'Component') : void 0;
    }

    return shouldUpdate;
    }

    if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState);
    }

    return true;//  shouldComponentUpdate 方法預設返回true
    }

render在哪裡?finishClassComponent程式碼片段。

   if (didCaptureError && (!enableGetDerivedStateFromCatch || typeof ctor.getDerivedStateFromCatch !== 'function')) {
    nextChildren = null;

    if (enableProfilerTimer) {
      stopBaseRenderTimerIfRunning();
    }
    } else {//沒有問題才會執行render函式
    {
      ReactDebugCurrentFiber.setCurrentPhase('render');
      nextChildren = instance.render();
      if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
        instance.render();// 這裡執行
      }
      ReactDebugCurrentFiber.setCurrentPhase(null);
    }
    }

finish之後會繼續判斷元件中是否還有其他元件,如果有繼續迴圈上面的過程,直到最後一個節點為null,然後才會一個一個的區執行ComponentDidMount或者ComponentDidUpdate方法,所以出現一個現象,子元件的Did字首的方法會先呼叫,就是這個原因。早些版本也是這樣是因為遞迴呼叫,現在這裡是一個無限迴圈,而且套了很多層。
還有更想深入瞭解React原始碼的童鞋,這裡有篇文章寫的非常不錯,他會在React整個架構上給你一個指導和思路,

本章的例項程式碼在study/lifeCycle資料夾下。工程原始碼地址,點選這裡