美文网首页
R-4.React生命周期及最新变动

R-4.React生命周期及最新变动

作者: LuckyHappyBug | 来源:发表于2018-10-22 20:46 被阅读0次

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

    相关文章

      网友评论

          本文标题:R-4.React生命周期及最新变动

          本文链接:https://www.haomeiwen.com/subject/ppexaftx.html