R-4.React生命周期及最新變動

這一章來介紹react組件的生命周期臼予。之前介紹過純函數(shù)組件是沒有生命周期的惠爽,那到底生命周期是什么?其實(shí)簡單來講就是組件的初始和消亡,就如同小草的生長一樣(配圖隨機(jī)哟旗,純屬護(hù)眼)從發(fā)芽到消亡。組件在這個過程中會經(jīng)歷那些階段,又是如何標(biāo)志這些階段的闸餐。


生命初始

本章的重點(diǎn)就是要搞明白的就是下面的重點(diǎn)以及思維導(dǎo)圖(前三點(diǎn)必須掌握):
1.組件生命周期有哪幾個階段饱亮?
2.每個階段又包含什么?
3.每個階段周期函數(shù)的調(diào)用順序什么舍沙?
*4.根據(jù)源碼探究為什么是這樣的順序近上?(感興趣的可以看看)


組件生命周期

1.組件掛載階段

組件掛載是指組件創(chuàng)建實(shí)例時所必須經(jīng)歷的一個過程,其中有三個函數(shù)是這一階段必須執(zhí)行的拂铡。如思維導(dǎo)圖所示壹无,分別是
componentWillMount():組件渲染之前執(zhí)行的函數(shù),且組件實(shí)例創(chuàng)建之后不再執(zhí)行感帅,即只執(zhí)行一次斗锭。
render():渲染組件,該函數(shù)同樣屬于更新階段失球,負(fù)責(zé)渲染岖是,無論是創(chuàng)建還是更新都需要重新渲染,就需要這個函數(shù)实苞。
componentDidMount():組件渲染之后執(zhí)行豺撑,且僅執(zhí)行一次。想拿到組件實(shí)例只能在該函數(shù)中以及執(zhí)行之后才可以黔牵。

2.組件更新階段

導(dǎo)致組件更新有兩種情況聪轿,父組件props發(fā)生變化,組件state值發(fā)生變化猾浦。組件更新階段稍微經(jīng)歷的函數(shù)多一點(diǎn)陆错,而且都帶了參數(shù)的,一定注意這些參數(shù)跃巡,寫組件的時候有大用危号。
componentWillReceiveProps(nextProps):該函數(shù)只在父組件創(chuàng)給子組件的屬性值牧愁,即子組件需要的props值發(fā)生變化時才會觸發(fā)執(zhí)行素邪。參數(shù)nextProps則是已經(jīng)改變了的props值。
shouldComponentUpdate(nextProps,nextState): 控制是否要更新組件猪半,他必須返回一個布爾值兔朦,false不更新,true更新磨确。不更新時則不再執(zhí)行更新階段下面的函數(shù)沽甥。所以,參數(shù)nextProps和nextState都是已經(jīng)改變的值乏奥,可根據(jù)他們判斷是否更新組件摆舟,由你自己掌握。
componentWillUpdate(nextProps,nextState): 更新組件渲染前執(zhí)行,參數(shù)與上一個函數(shù)參數(shù)一致恨诱。
render(): 渲染更新的組件
componentDidUpdate(preProps,preState): 組件更新后即重新渲染后執(zhí)行媳瞪,注意參數(shù),它是props和state變化前的值照宝。組件中如果有新出現(xiàn)的DOM結(jié)構(gòu)蛇受,也只能在這個函數(shù)執(zhí)行之后才能拿到實(shí)例。

3.卸載階段

卸載就比較簡單了厕鹃,只有一個函數(shù)兢仰。componentWillUnmount.

生命周期是不是很簡單,就這幾個函數(shù)剂碴,記住執(zhí)行順序把将,以及觸發(fā)條件,干了什么汗茄。你就已經(jīng)基本掌握了生命周期了秸弛。生命周期的代碼這里就不貼了,去文章后面找到Github地址洪碳,下載源代碼递览,既可以看到實(shí)例,也可以跑出來看效果瞳腌。

4.React生命周期的變動

很尷尬绞铃,之前做筆記的時候看的源碼現(xiàn)在已經(jīng)不合時宜了,react從16版本開始有了比較大的改動嫂侍,現(xiàn)在已經(jīng)是16.4.2版本儿捧,剛?cè)タ匆幌鹿倬W(wǎng)最新的已經(jīng)是16.5.2。源碼部分非常多挑宠,這里不再貼源代碼菲盾。我僅提煉最重要的進(jìn)行說明。

第一點(diǎn)各淀,生命周期中的多了兩個函數(shù):static getDerivedStateFromProps懒鉴,getSnapshotBeforeUpdate。

第二點(diǎn)碎浇,生命周期將在react17中徹底取消componentWillMount临谱、componentWillReceiveProps和componentWillUpdate三個生命周期函數(shù)。17版本之前原生命周期依然保留奴璃,并添加了三個對應(yīng)的帶有UNSAFE_前綴的三個周期函數(shù)悉默;且如果同時定義了getDerivedStateFromProps,則只會運(yùn)行g(shù)etDerivedStateFromProps。

mountClassInstance中的代碼片段苟穆,組件掛載時抄课,前兩個if正是用getDerivedStateFromProps替換掉了掛載時的componentWillMount

// 組件掛載時先判斷是否定義這個靜態(tài)函數(shù)唱星,如果定義了,則不再執(zhí)行componentWillMount方法
var getDerivedStateFromProps = workInProgress.type.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(workInProgress, getDerivedStateFromProps, props);// 執(zhí)行這個getDerivedStateFromProps方法
instance.state = workInProgress.memoizedState;
}
// ctor = workInProgress.type;如果定義了getDerivedStateFromProps則不再執(zhí)行里面的函數(shù)跟磨。
if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
// 如果沒有定義getDeriveStateFromProps則執(zhí)行此方法魏颓,此方法會判斷是否定義了componentWillMount方法,如果定義了則會執(zhí)行
callComponentWillMount(workInProgress, instance);
updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
  processUpdateQueue(workInProgress, updateQueue, props, instance, renderExpirationTime);
  instance.state = workInProgress.memoizedState;
}
}

updateClassInstance中的代碼片段吱晒,組件state和props變化引起組件更新如何替換掉了componentWillReceiveProps函數(shù)

var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
// 是否定義了新的生命周期函數(shù),如果定義了則在callComponentWillReceiveProps甸饱,該方法內(nèi)會判斷是否定義了UNSAFE_前綴的以及不加前綴的componentWillReceiveProps方法,并執(zhí)行仑濒。
var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
// 定義了新的生命周期函數(shù)則不再執(zhí)行callComponentWillReceiveProps
if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
if (oldProps !== newProps || oldContext !== newContext) {
  callComponentWillReceiveProps(workInProgress, instance, newProps, newContext);
}
}

同樣在updateClassInstance中叹话,按照順序,componentWillReceiveProps執(zhí)行之后是shouldComponentUpdate墩瞳,并且傳入新的state和props驼壶。這里也是一樣的,沒有變化喉酌。

// 判斷是否定義了新函數(shù)热凹,定義了則去執(zhí)行
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(workInProgress, getDerivedStateFromProps, newProps);
newState = workInProgress.memoizedState;
}
// 判斷是否更新組件,checkShouldComponentUpdate方法源碼在下面
var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext);

if (shouldUpdate) {// 根據(jù)是否要更新來決定是否執(zhí)行下面的代碼
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
// hasNewLifecycles就是上一個代碼片段中的變量泪电,組件中如果用新的生命周期函數(shù)則為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當(dāng)前組件的實(shí)例
  }
  if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
    instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
  }
  stopPhaseTimer();
}
// 這里跟之前的版本很不一樣般妙,之前是會先經(jīng)過render,然后是在這里立即判斷是否定義了componentDidUpdate函數(shù)相速,然后立即執(zhí)行
if (typeof instance.componentDidUpdate === 'function') {
  workInProgress.effectTag |= Update;// 現(xiàn)在則是通過位運(yùn)算先標(biāo)記碟渺,render之后在執(zhí)行。
}
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') {// 定義了則執(zhí)行
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 方法默認(rèn)返回true
}

render在哪里苫拍?finishClassComponent代碼片段。

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

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

finish之后會繼續(xù)判斷組件中是否還有其他組件旺隙,如果有繼續(xù)循環(huán)上面的過程绒极,直到最后一個節(jié)點(diǎn)為null,然后才會一個一個的區(qū)執(zhí)行ComponentDidMount或者ComponentDidUpdate方法蔬捷,所以出現(xiàn)一個現(xiàn)象垄提,子組件的Did前綴的方法會先調(diào)用,就是這個原因抠刺。早些版本也是這樣是因?yàn)檫f歸調(diào)用塔淤,現(xiàn)在這里是一個無限循環(huán)摘昌,而且套了很多層速妖。
還有更想深入了解React源碼的童鞋,這里有篇文章寫的非常不錯聪黎,他會給你在React整個架構(gòu)上給你一個指導(dǎo)和思路罕容,點(diǎn)擊React源碼速覽

本章的實(shí)例代碼在study/lifeCycle文件夾下备恤。工程源碼地址,點(diǎn)擊這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锦秒,一起剝皮案震驚了整個濱河市露泊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旅择,老刑警劉巖惭笑,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異生真,居然都是意外死亡沉噩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門柱蟀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來川蒙,“玉大人,你說我怎么就攤上這事长已⌒笳#” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵术瓮,是天一觀的道長康聂。 經(jīng)常有香客問我,道長胞四,這世上最難降的妖魔是什么早抠? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮撬讽,結(jié)果婚禮上蕊连,老公的妹妹穿的比我還像新娘。我一直安慰自己游昼,他們只是感情好甘苍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烘豌,像睡著了一般载庭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上廊佩,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天囚聚,我揣著相機(jī)與錄音,去河邊找鬼标锄。 笑死顽铸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的料皇。 我是一名探鬼主播谓松,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼星压,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鬼譬?” 一聲冷哼從身側(cè)響起娜膘,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎优质,沒想到半個月后竣贪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巩螃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年贾富,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牺六。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡颤枪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淑际,到底是詐尸還是另有隱情畏纲,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布春缕,位于F島的核電站盗胀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏锄贼。R本人自食惡果不足惜票灰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宅荤。 院中可真熱鬧屑迂,春花似錦、人聲如沸冯键。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惫确。三九已至手报,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間改化,已是汗流浹背掩蛤。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陈肛,地道東北人揍鸟。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像燥爷,于是被迫代替她去往敵國和親蜈亩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容