R-4.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.4.2版本,刚去看一下官网最新的已经是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整个架构上给你一个指导和思路,点击React源码速览

本章的实例代码在study/lifeCycle文件夹下。工程源码地址,点击这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,511评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,011评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,489评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,872评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,741评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,525评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,914评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,542评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,833评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,858评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,674评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,479评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,923评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,099评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,400评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,923评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,141评论 2 339

推荐阅读更多精彩内容