一次性搞懂解React生命周期

1. 初步了解React生命周期

React生命周期能够分为挂载、更新、卸载三个阶段。主要能够分为两类:react

  • 组件挂载和卸载;
  • 组件接收新的数据和状态时的更新;

1.1 组件的挂载

组件的挂载是最基本过程,这个过程主要作初始化。在这初始化个过程当中componentWillMount会在render方法以前执行,而componentDidMount方法会在render方法以后执行。分别表明了渲染先后时刻。写一个简单的例子:app

class Demo extends React.Component {
    static propTypes = {}
    static defaultProps = {}
    constructor(props) {
        super(props)
        this.state = {}
    }
    componentWillMount() {}
    render() {return null}
    componentDidMount() {}
}

如上,这个初始化过程没有什么特别之处,这里包括读取初始state读取初始props、以及两个生命周期方法componentWillMountcomponentDidMount。这些都只会在组件初始化时执行一次less

1.2 组件的卸载

组件的卸载只有componentWillUnmount这个一个方法。异步

1.3 组件的更新

组件的更新发生在父组件传递props或者自身执行setState改变状态这一系列操做的状况下。和组件更新的生命周期方法有如下几个:函数

class Demo extends React.Component {
    //当组件更新时会顺序执行如下方法
    componentWillReceiveProps(nextProps){} //
    shouldComponentUpdate(nextProps, nextState) {} //返回false则中止向下执行,默认返回true
    componentWillUpdate(nextProps, nextState) {}
    render() {}
    componentDidUpdate(prevProps, prevState) {}
}
tip: shouldComponentUpdate能够用来正确的渲染组件的。理想状况下,父级节点改变时,只会从新渲染一条链路上和该props相关的组件。 可是默认状况下,React会渲染全部的节点,由于shouldComponentUpdate默认返回true。

clipboard.png

2. 深刻了解React生命周期

前面大体介绍了组件的生命周期主要分为三种状态:挂载、更新、卸载。以下图能够详细了解不一样状态的执行顺序:oop

clipboard.png

使用ES6 classes构建组件的时候static defaultProps={}其实就是调用内部的getDefaultProps方法。constructor中的this.state={}其实就是调用内部的getInitialState方法。this

2.1 详解React生命周期

自定义组件生命周期经过3个阶段进行控制:MOUNTING,RECEIVE_PROPSUNMOUNTING,它负责通知组件当时所处的阶段,应该执行生命周期中的哪一个步骤。这三个阶段分别对应三个方法:spa

clipboard.png

2.2使用createClass建立自定义组件

createClass是建立自定义组件的入口方法,负责管理生命周期中的getDefaultProps方法。该方法在整个生命周期中只执行一次,这样全部实例初始化的props都能共享。
经过createClass建立自定义组件,利用原型继承ReactClassComponent父类,按顺序合并mixin,设置初始化defaultProps,返回构造函数。prototype

var ReactClass = {
  createClass: function(spec) {
    var Constructor = function(props, context, updater) {
  
      // 自动绑定
      if (this.__reactAutoBindPairs.length) {
        bindAutoBindMethods(this);
      }
  
      this.props = props;
      this.context = context;
      this.refs = emptyObject;
      this.updater = updater || ReactNoopUpdateQueue;
  
      this.state = null;
  
      //ReactClasses没有构造函数,经过getInitialState和componentWillMount来代替
      var initialState = this.getInitialState ? this.getInitialState() : null;
      this.state = initialState;
    };

    //原型继承ReactClassComponent父类
    Constructor.prototype = new ReactClassComponent();
    Constructor.prototype.constructor = Constructor;
    Constructor.prototype.__reactAutoBindPairs = [];
  
    //合并mixin
    injectedMixins.forEach(
      mixSpecIntoComponent.bind(null, Constructor)
    );
  
    mixSpecIntoComponent(Constructor, spec);
  
    //全部mixin合并后初始化defaultProps(在生个生命周期中,defaultProps只执行一次)
    if (Constructor.getDefaultProps) {
      Constructor.defaultProps = Constructor.getDefaultProps();
    }
    //设置原型
    for (var methodName in ReactClassInterface) {
      if (!Constructor.prototype[methodName]) {
        Constructor.prototype[methodName] = null;
      }
    }
     //最后返回的是构造函数
    return Constructor;
  },
}

2.3 阶段一:MOUNTING

mountComponent负责管理生命周期中的getInitialState,componentWillMount,rendercomponentDidMount
因为getDefaultProps是在初始化构造函数中进行管理的,因此也是整个生命周期中最早执行的。并且只执行一次也能够理解了。3d

因为经过ReactCompositeComponentBase返回的是一个虚拟节点,因此须要经过 instantiate-ReactComponent去获得实例,在经过mountComponent拿到结果做为当前自定义元素的结果。

经过mountComponent挂载组件,初始化序号,标记参数等,判断是否为无状态组件,并进行对应的初始化操做,好比初始化props,context等参数。利用getInitialState获取初始化state, 初始化更新队列和更新状态。

若是存在componentWillMount则执行,若是此时在componetWillMount调用setState方法,是不会触发re-render方法,而是会进行state合并,且inst.state = this._processPendingState(inst.props, inst.context)componentWillMount以后执行。所以在render中才能够获取到最新的state。

所以,React是经过更新队列this._pendingStateQueue以及更新状态this._pendingReeplaceStatethis._pendingForUpdate来实现setState的异步更新。

当渲染完成后,若存在componentDidMount则调用。

其实mountComponent是经过递归渲染内容。因为递归的特性,父组件的componentWillMount在其子组件的componentWillMount以前调用,父组件的componentDidMount在其子组件的componentDidMount以后调用。
clipboard.png

//react/src/renderers/shared/reconciler/ReactCompositeComponent.js

//当组件挂载时,会分配一个递增编号,表示执行ReactUpdates时更新组件的顺序
var nextMountID = 1
var ReactCompositeComponentMixin = {
  //初始化组件,渲染标记,注册事件监听器
  mountComponent: function (transaction, nativeParent, nativeContainerInfo, context) {
    this._context = context; //当前组件对应的上下文
    this._mountOrder = nextMountID++;
    this._nativeParent = nativeParent;
    this._nativeContainerInfo = nativeContainerInfo;

    var publicProps = this._processProps(this._currentElement.props);
    var publicContext = this._processContext(context);

    var Component = this._currentElement.type;

    // 初始化公共类
    var inst;
    var renderedElement;

    //这里判断是不是无状态组件,无状态组件没有更新状态序列,只关注更新
    if (Component.prototype && Component.prototype.isReactComponent) {
      inst = new Component(publicProps, publicContext, ReactUpdateQueue);
    } else {
      inst = Component(publicProps, publicContext, ReactUpdateQueue);
      if (inst == null || inst.render == null) {
        renderedElement = inst;
        warnIfInvalidElement(Component, renderedElement);
        invariant(
          inst === null ||
          inst === false ||
          ReactElement.isValidElement(inst),
          '%s(...): A valid React element (or null) must be returned. You may have ' +
          'returned undefined, an array or some other invalid object.',
          Component.displayName || Component.name || 'Component'
        );
        inst = new StatelessComponent(Component);
      }
    }



    // These should be set up in the constructor, but as a convenience for
    // simpler class abstractions, we set them up after the fact.
    //这些初始化参数应该在构造函数中设置,再此处设置为了便于简单的类抽象
    inst.props = publicProps;
    inst.context = publicContext;
    inst.refs = emptyObject;
    inst.updater = ReactUpdateQueue;

    this._instance = inst;

    // 将实例存储为一个引用
    ReactInstanceMap.set(inst, this);

    //初始化state
    var initialState = inst.state;
    if (initialState === undefined) {
      inst.state = initialState = null;
    }

    //初始化state更新队列
    this._pendingStateQueue = null;
    this._pendingReplaceState = false;
    this._pendingForceUpdate = false;

    var markup;
    //若是挂载错误则执行performInitialMountWithErrorHandling(方法以下)
    if (inst.unstable_handleError) {
      markup = this.performInitialMountWithErrorHandling(
        renderedElement,
        nativeParent,
        nativeContainerInfo,
        transaction,
        context
      );
    } else {
      //执行挂载
      markup = this.performInitialMount(renderedElement, nativeParent, nativeContainerInfo, transaction, context);
    }

    //若是存在componentDidMount则调用
    if (inst.componentDidMount) {
      transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
    }

    return markup;
  },
  //挂载错误执行方法
  performInitialMountWithErrorHandling: function (
    renderedElement,
    nativeParent,
    nativeContainerInfo,
    transaction,
    context
  ) {
    var markup;
    var checkpoint = transaction.checkpoint();
    try {
      //若是没有错误则初始化挂载
      markup = this.performInitialMount(renderedElement, nativeParent, nativeContainerInfo, transaction, context);
    } catch (e) {
      // Roll back to checkpoint, handle error (which may add items to the transaction), and take a new checkpoint
      transaction.rollback(checkpoint);
      this._instance.unstable_handleError(e);
      if (this._pendingStateQueue) {
        this._instance.state = this._processPendingState(this._instance.props, this._instance.context);
      }
      checkpoint = transaction.checkpoint();
      //若是捕捉到错误,则执行unmountComponent后再初始化挂载
      this._renderedComponent.unmountComponent(true);
      transaction.rollback(checkpoint);
      markup = this.performInitialMount(renderedElement, nativeParent, nativeContainerInfo, transaction, context);
    }
    return markup;
  },
  //初始化挂载方法
  performInitialMount: function(renderedElement, nativeParent, nativeContainerInfo, transaction, context) {
    var inst = this._instance;
    //若是存在componentWillMount则调用
    if (inst.componentWillMount) {
      inst.componentWillMount();
      //若是在componentWillMount触发setState时,不会触发re-render,而是自动提早合并
      if (this._pendingStateQueue) {
        inst.state = this._processPendingState(inst.props, inst.context);
      }
    }

    // 若是不是无状态组件则直接渲染
    if (renderedElement === undefined) {
      renderedElement = this._renderValidatedComponent();
    }

    this._renderedNodeType = ReactNodeTypes.getType(renderedElement);
    //获得 _currentElement对应的component类实例
    this._renderedComponent = this._instantiateReactComponent(
      renderedElement
    );

    //递归渲染
    var markup = ReactReconciler.mountComponent(
      this._renderedComponent,
      transaction,
      nativeParent,
      nativeContainerInfo,
      this._processChildContext(context)
    );

    return markup;
  }
}

2.4 阶段二:REVEIVE_PROPS

updateComponent负责管理生命周期的componentWillReceivePropsshouldComponentcomponentWillUpdaterendercomponentDidUpdate

首先经过updateComponent更新组件,若是先后元素不一致,说明须要组件更新。

若存在componentWillReceiveProps,则执行。若是此时在componentWillReceiveProps中调用setState是不会触发re-render,而是会进行state合并。且在componentWillReceiveProps,shouldComponentUpate和componentWillUpdate是没法获取更新后的this.state。须要设置inst.state = nextState后才能够。所以只有在render和componentDidUpdate中才能够获取更新后的state.

调用shouldComponentUpdate判断是否须要进行组件更新,若是存在componentWillUpdate则执行。

updateComponet也是经过递归渲染的,因为递归的特性,父组件的componentWillUpdate在子组件以前执行,父组件的componentDidUpdate在其子组件以后执行。

clipboard.png

2.5 阶段三:UNMOUNTING

unmountComponent负责管理componentWillUnmount。在这个阶段会清空一切。

//组件卸载
  unmountComponent: function(safely) {
    if (!this._renderedComponent) {
      return;
    }
    var inst = this._instance;

    //若是存在componentWillUnmount,则调用
    if (inst.componentWillUnmount) {
      if (safely) {
        var name = this.getName() + '.componentWillUnmount()';
        ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst));
      } else {
        inst.componentWillUnmount();
      }
    }

    //若是组件已经渲染,则对组件进行unmountComponent操做
    if (this._renderedComponent) {
      ReactReconciler.unmountComponent(this._renderedComponent, safely);
      this._renderedNodeType = null;
      this._renderedComponent = null;
      this._instance = null;
    }

    //重置相关参数,更新队列以及更新状态
    this._pendingStateQueue = null;
    this._pendingReplaceState = false;
    this._pendingForceUpdate = false;
    this._pendingCallbacks = null;
    this._pendingElement = null;
    this._context = null;
    this._rootNodeID = null;
    this._topLevelWrapper = null;
    //清除公共类
    ReactInstanceMap.remove(inst);
  },
相关文章
相关标签/搜索