美文网首页程序员让前端飞
从源码分析React实例化生命周期

从源码分析React实例化生命周期

作者: 陈小俊先生 | 来源:发表于2017-08-23 13:46 被阅读0次

原文链接:链接

React实例化生命周期可以说相当简单了,想象你随随便便都能背出来。但是!要是从源码来追踪、并分析的话可以说是相当复杂了,不信你往下看。

首先得从元祖ReactElement说起,知道ReactElement是个啥玩意儿么?他就是常说的virtual dom。

在react中,ReactElement(常说的virtual dom) 可以通过react定义的方法createElement或者createClass创建。

createElement源码:

ReactElement.createElement = function(type, config, children) {
  var propName;

  // Reserved names are extracted
  var props = {};

  var key = null;
  var ref = null;
  var self = null;
  var source = null;
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
};

如下代码调用createElement

ReactDOM.render(
    <h1>Hello World</h1>,
    document.getElementById('root')
);

createClass源码(删减):

function createClass(spec) {
  var Constructor = identity(function(props, context, updater) {
    this.props = props;
    this.context = context;
    this.refs = emptyObject;
    this.updater = updater || ReactNoopUpdateQueue;

    this.state = null;

    var initialState = this.getInitialState ? this.getInitialState() : null;
   
    this.state = initialState;
  });
  Constructor.prototype = new ReactClassComponent();
  Constructor.prototype.constructor = Constructor;
  Constructor.prototype.__reactAutoBindPairs = [];

  injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));

  mixSpecIntoComponent(Constructor, IsMountedPreMixin);
  mixSpecIntoComponent(Constructor, spec);
  mixSpecIntoComponent(Constructor, IsMountedPostMixin);

  if (Constructor.getDefaultProps) {
    Constructor.defaultProps = Constructor.getDefaultProps();
  }

  return Constructor;
}

如下代码调用createClass

ReactDOM.render(
    <App />
    document.getElementById('root')
);

createElement创建的是DOM Element,而createClass创建的是Component Elements。

当ReactElement的type属性为字符串时,它代表是DOM Element,如下:

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

当ReactElement的type属性为一个函数或一个类时,它代表Component Elements,如下:

class Button extends React.Component {
  render() {
    const { children, color } = this.props;
    return {
      type: 'button',
      props: {
        className: 'button button-' + color,
        children: {
          type: 'b',
          props: {
            children: children
          }
        }
      }
    };
  }
}

// Component Elements
{
  type: Button,
  props: {
    color: 'blue',
    children: 'OK!'
  }
}

到这里可以得出第一个结论:

  1. 组件生命周期函数getDefaultProps、getInitialState是在createClass里调用的。

react通过调用createClass或者createElement,生成一个ReactElement。接下来我们探讨ReactElement的走向和变装。

在_renderNewRootComponent函数中,ReactElement经过了一个工厂函数instantiateReactComponent的打包。
var componentInstance = instantiateReactComponent(nextElement, false);

我们看一下instantiateReactComponent的关键代码:

function instantiateReactComponent(node, shouldHaveDebugID) {
  var instance;

  if (node === null || node === false) {
    instance = ReactEmptyComponent.create(instantiateReactComponent);
  } else if (typeof node === 'object') {
    var element = node;
    var type = element.type;

    if (typeof element.type === 'string') {
      instance = ReactHostComponent.createInternalComponent(element);
    } else if (isInternalComponentType(element.type)) {

      instance = new element.type(element);

      if (!instance.getHostNode) {
        instance.getHostNode = instance.getNativeNode;
      }
    } else {
      instance = new ReactCompositeComponentWrapper(element);
    }
  } else if (typeof node === 'string' || typeof node === 'number') {
    instance = ReactHostComponent.createInstanceForText(node);
  } else {
    invariant(false, 'Encountered invalid React node of type %s', typeof node);
  }

  instance._mountIndex = 0;
  instance._mountImage = null;

  return instance;
}

我们已经知道ReactElement有不同的type,instantiateReactComponent根据这个type的不同,调用相应的ReactComponent构造函数。

这里我把ReactComponent分成4类:

  1. ReactDOMEmptyComponent:空对象
  2. ReactDOMComponent:DOM原生对象
  3. ReactCompositeComponent:React自定义对象
  4. ReactDOMTextComponent:文本对象

值得注意的是,instantiateReactComponent并不直接生成一个ReactComponent,而是将ReactElement包装成一个 ReactCompositeComponentWrapper,包在了一个容器中。因此,ReactCompositeComponentWrapper除了有他自己的属性外,还能调用生成他的构造函数的一些方法(这样设计一方面就是为了共享这些方法)。

这个所谓的容器长这样:

{
    _calledComponentWillUnmount: false
    _compositeType: null
    _context: null
    _currentElement: {$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: ƒ, …}
    _debugID: 0
    _hostContainerInfo: null
    _hostParent: null
    _instance: null
    _mountImage: null
    _mountIndex: 0
    _mountOrder: 0
    _pendingCallbacks: null
    _pendingElement: null
    _pendingForceUpdate: false
    _pendingReplaceState: false
    _pendingStateQueue: null
    _renderedComponent: null
    _renderedNodeType:     null
    _rootNodeID: 0
    _topLevelWrapper: null
    _updateBatchNumber: null
    _warnedAboutRefsInRender: false
    __proto
}

这里先留一个疑问:为什么要把ReactElement放在容器中?

继续,_constructComponent是ReactCompositeComponentWrapper(实例)的构造函数中的一个方法,这个方法能生成一个ReactComponent,这个ReactComponent就是我们写的组件的实例。

var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);

一个inst长这样:

{
    context: undefined
    props: {propsData: "propsData"}
    refs: {}
    state: {stateData: "stateData"}
    updater: ...
    enqueueSetState: ...
    isMounted: (...)
    replaceState: (...)
    __proto__: ReactComponent
}

之后,在performInitialMount方法中悄悄的执行了inst.componentWillMount();,componentWillMount就是在这里跟render和componentDidMount先走一步的。

再之后,还是在mountComponent中,有这么一段代码:

if (inst.componentDidMount) {
  if (process.env.NODE_ENV !== 'production') {
    transaction.getReactMountReady().enqueue(function () {
      measureLifeCyclePerf(function () {
        return inst.componentDidMount();
      }, _this._debugID, 'componentDidMount');
    });
  } else {
        transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
  }
}

此时但是if (inst.componentDidMount)肯定为true,但是他并不会执行componentDidMount(),这是为什么呢?我们往下走。

mountComponent返回的是一个markup,一个markup长这样:

{
    children:[]
    html:null
    node:h1
    text:null
    toString:ƒ toString()
    __proto__:Object
}

markup经过mountComponentIntoNode、_mountImageIntoNode,执行setInnerHTML(container, markup);终于渲染出来了!!!蓝瘦香菇有木有啊!!!

之后其实经过Transaction里的perform方法的,经过之后transaction.getReactMountReady()相当于true了。这个时候就能访问componentDidMount了。

至此,React初次实例化的一个生命周期分析完毕。起立、敬礼。

经过以上分析,才能得出以下众所周知的总结:

当组件(由createClass创建)在客户端被实例化,第一次被创建时,以下方法依次被调用:

  1. getDefaultProps
  2. getInitialState
  3. componentWillMount
  4. render
  5. componentDidMount

相关文章

  • React组件生命周期

    react组件生命周期一个React组件的生命周期分为实例化,存在期,销毁 实例化 组件在客户端被实例化,第一次被...

  • 从源码分析React实例化生命周期

    原文链接:链接 React实例化生命周期可以说相当简单了,想象你随随便便都能背出来。但是!要是从源码来追踪、并分析...

  • react生命周期(一分钟秒懂)

    毫不夸张的说,react的生命周期搞清楚了,react就会了一半。 实例化 首次实例化 getDefaultPro...

  • React组建的生命周期

    React 生命周期 React生命周期主要包括4个阶段: 初始化阶段 实例化阶段 更新阶段 销毁阶段 1 设置组...

  • Glide源码详解

    一、源码解析 (1)Glide.with(this):初始化、绑定生命周期、获得RequestManager实例 ...

  • React Native组件生命周期

    一、看图分析 React Native组件的生命周期大致上可以划分为实例化阶段、存在阶段和销毁阶段,其中最常用的为...

  • react-native 生命周期

    (一)、图解 基本总结: 从图看:在React Native中,组件的生命周期大致可以分为3个阶段(实例化阶段,存...

  • [转]react-native 生命周期

    (一)、图解 基本总结: 从图看:在React Native中,组件的生命周期大致可以分为3个阶段(实例化阶段,存...

  • react组件的生命周期

    组件的生命周期 组件在react的生命周期中主要经历三个阶段:实例化、存在期和销毁时。React.js在组件生命周...

  • 2020 前端面试题 react

    1、react 生命周期函数 初始化阶段: getDefaultProps:获取实例的默认属性 getInitia...

网友评论

    本文标题:从源码分析React实例化生命周期

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