這一章來介紹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)擊這里