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文件夹下。工程源码地址,点击这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值