react源码解读 {createClass}

对一个框架源码的解读,既有利于更深切地相识框架,运用上更随心所欲,又能够进修到个中代码组织的思绪,吸取其英华简约的写法以便于一样平常工作上运用。下面我就遴选近来几年大热点react(15.3.1),从中理会框架的设想思绪,由浅入深地进修。

我们从这个文件最先看起,这是react的主进口(./lib/react.js)。

/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule React
 */

'use strict';

var _assign = require('object-assign');

var ReactChildren = require('./ReactChildren');
var ReactComponent = require('./ReactComponent');
var ReactPureComponent = require('./ReactPureComponent');
var ReactClass = require('./ReactClass');
var ReactDOMFactories = require('./ReactDOMFactories');
var ReactElement = require('./ReactElement');
var ReactPropTypes = require('./ReactPropTypes');
var ReactVersion = require('./ReactVersion');

var onlyChild = require('./onlyChild');
var warning = require('fbjs/lib/warning');

var createElement = ReactElement.createElement;
var createFactory = ReactElement.createFactory;
var cloneElement = ReactElement.cloneElement;

if (process.env.NODE_ENV !== 'production') {
  var ReactElementValidator = require('./ReactElementValidator');
  createElement = ReactElementValidator.createElement;
  createFactory = ReactElementValidator.createFactory;
  cloneElement = ReactElementValidator.cloneElement;
}

var __spread = _assign;

if (process.env.NODE_ENV !== 'production') {
  var warned = false;
  __spread = function () {
    process.env.NODE_ENV !== 'production' ? warning(warned, 'React.__spread is deprecated and should not be used. Use ' + 'Object.assign directly or another helper function with similar ' + 'semantics. You may be seeing this warning due to your compiler. ' + 'See https://fb.me/react-spread-deprecation for more details.') : void 0;
    warned = true;
    return _assign.apply(null, arguments);
  };
}

var React = {

  // Modern

  Children: {
    map: ReactChildren.map,
    forEach: ReactChildren.forEach,
    count: ReactChildren.count,
    toArray: ReactChildren.toArray,
    only: onlyChild
  },

  Component: ReactComponent,
  PureComponent: ReactPureComponent,

  createElement: createElement,
  cloneElement: cloneElement,
  isValidElement: ReactElement.isValidElement,

  // Classic

  PropTypes: ReactPropTypes,
  createClass: ReactClass.createClass,
  createFactory: createFactory,
  createMixin: function (mixin) {
    // Currently a noop. Will be used to validate and trace mixins.
    return mixin;
  },

  // This looks DOM specific but these are actually isomorphic helpers
  // since they are just generating DOM strings.
  DOM: ReactDOMFactories,

  version: ReactVersion,

  // Deprecated hook for JSX spread, don't use this for anything.
  __spread: __spread
};

module.exports = React;

我们直接跳过前面的环境推断以及模块引入,能够看到从50行起就是React的症结代码。而且我们能够清楚的从上面看到React所供应的要领。这是离我们运用者近来的一层,看到信息量不多。我们就根据开辟的思绪,一步一步地深切源码。
编写一个组件,固然是从建立最先,我们运用的是 React.createClass,不难发明,React.createClass实际上援用的是ReactClass.createClass。固然我们也能够用ES6的写法直接继承至React.Component.这两种写法有什么差别存在,我们先把牵挂放在背面。
先从createClass的源码看起(./lib/ReactClass)。

var ReactClass = {

  /**
   * Creates a composite component class given a class specification.
   * See https://facebook.github.io/react/docs/top-level-api.html#react.createclass
   *
   * @param {object} spec Class specification (which must define `render`).
   * @return {function} Component constructor function.
   * @public
   */
  createClass: function (spec) {
    var Constructor = function (props, context, updater) {
      // This constructor gets overridden by mocks. The argument is used
      // by mocks to assert on what gets mounted.

      if (process.env.NODE_ENV !== 'production') {
        process.env.NODE_ENV !== 'production' ? warning(this instanceof Constructor, 'Something is calling a React component directly. Use a factory or ' + 'JSX instead. See: https://fb.me/react-legacyfactory') : void 0;
      }

      // Wire up auto-binding
      if (this.__reactAutoBindPairs.length) {
        bindAutoBindMethods(this);
      }

      this.props = props;
      this.context = context;
      this.refs = emptyObject;
      this.updater = updater || ReactNoopUpdateQueue;

      this.state = null;

      // ReactClasses doesn't have constructors. Instead, they use the
      // getInitialState and componentWillMount methods for initialization.

      var initialState = this.getInitialState ? this.getInitialState() : null;
      if (process.env.NODE_ENV !== 'production') {
        // We allow auto-mocks to proceed as if they're returning null.
        if (initialState === undefined && this.getInitialState._isMockFunction) {
          // This is probably bad practice. Consider warning here and
          // deprecating this convenience.
          initialState = null;
        }
      }
      !(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getInitialState(): must return an object or null', Constructor.displayName || 'ReactCompositeComponent') : _prodInvariant('82', Constructor.displayName || 'ReactCompositeComponent') : void 0;

      this.state = initialState;
    };
    Constructor.prototype = new ReactClassComponent();
    Constructor.prototype.constructor = Constructor;
    Constructor.prototype.__reactAutoBindPairs = [];

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

    mixSpecIntoComponent(Constructor, spec);

    // Initialize the defaultProps property after all mixins have been merged.
    if (Constructor.getDefaultProps) {
      Constructor.defaultProps = Constructor.getDefaultProps();
    }

    if (process.env.NODE_ENV !== 'production') {
      // This is a tag to indicate that the use of these method names is ok,
      // since it's used with createClass. If it's not, then it's likely a
      // mistake so we'll warn you to use the static property, property
      // initializer or constructor respectively.
      if (Constructor.getDefaultProps) {
        Constructor.getDefaultProps.isReactClassApproved = {};
      }
      if (Constructor.prototype.getInitialState) {
        Constructor.prototype.getInitialState.isReactClassApproved = {};
      }
    }

    !Constructor.prototype.render ? process.env.NODE_ENV !== 'production' ? invariant(false, 'createClass(...): Class specification must implement a `render` method.') : _prodInvariant('83') : void 0;

    if (process.env.NODE_ENV !== 'production') {
      process.env.NODE_ENV !== 'production' ? warning(!Constructor.prototype.componentShouldUpdate, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', spec.displayName || 'A component') : void 0;
      process.env.NODE_ENV !== 'production' ? warning(!Constructor.prototype.componentWillRecieveProps, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', spec.displayName || 'A component') : void 0;
    }

    // Reduce time spent doing lookups by setting these on the prototype.
    for (var methodName in ReactClassInterface) {
      if (!Constructor.prototype[methodName]) {
        Constructor.prototype[methodName] = null;
      }
    }

    return Constructor;
  },

  injection: {
    injectMixin: function (mixin) {
      injectedMixins.push(mixin);
    }
  }

};

644行起,createClass要领起首定义了一个Constructor组织函数,摺叠内部,我们看看这个要领在返回一个组织函数前做了什么,
直接跳到681行,组织函数的prototype指向一个ReactClassComponent的实例。

   Constructor.prototype = new ReactClassComponent();

往上翻我们能够发明,ReactClassComponent的prototype属性,拷贝了ReactComponent.prototype 和 ReactClassMixin,因而我们的组件能够运用ReactComponent原型上的要领。

var ReactClassComponent = function () {};
_assign(ReactClassComponent.prototype, ReactComponent.prototype, ReactClassMixin);

683行到687行。
定义了 __reactAutoBindPairs 为一个空数组。
先将mixin内里的要领根据key,function内容的递次成对存入 __reactAutoBindPairs ,
接着就是spec对象里的要领用一样的体式格局存入。

    Constructor.prototype.__reactAutoBindPairs = [];

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

    mixSpecIntoComponent(Constructor, spec);

690行我们能够看到Constructor.defaultProps 就是我们开辟中 getDefaultProps()所返回的对象。

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

694行 — 712行 是在开辟环境中对开辟者的发起,以及范例运用的警示。
715行 — 719行 能够晓得我们建立一个组件须要定义的要领都在ReactClassInterface上有,当前未定义的要领设置为空,我们就能够经由过程打印组件的prototype属性清楚地在日记上晓得我们有哪些api是未定义的。经由过程设置未定义的属性为空,能够削减顺序查找的时刻。
721行 终究返回了这个封装好的组织函数。

    for (var methodName in ReactClassInterface) {
      if (!Constructor.prototype[methodName]) {
        Constructor.prototype[methodName] = null;
      }
    }

    return Constructor;

看到这里我们能够邃晓一点,组件实质上是一个组织函数,而我们自定义的要领,既存在了prototype里,也根据[key,content,key,content…]的体式格局归结到了Constructor.prototype.__reactAutoBindPairs 里。这是为了组件实例化时能够将这些要领直接遍历绑定在实例上,而且避免了React官方指定的要领也被绑定在实例上。

接下来我们睁开645行的Constructor,能够看到实例化的时刻重要做了两件事。
654行
第一件事就是将上文提到的存在Constructor.prototype.__reactAutoBindPairs 的内容成对掏出,绑定在实例上。

      if (this.__reactAutoBindPairs.length) {
        bindAutoBindMethods(this);
      }

668行 ——679行
第二件事就是推断组件是不是有定义getInitialState,假如有,则将state设置为该要领返回的值,假如没有设置state为null。

      var initialState = this.getInitialState ? this.getInitialState() : null;
      if (process.env.NODE_ENV !== 'production') {
        // We allow auto-mocks to proceed as if they're returning null.
        if (initialState === undefined && this.getInitialState._isMockFunction) {
          // This is probably bad practice. Consider warning here and
          // deprecating this convenience.
          initialState = null;
        }
      }
      !(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getInitialState(): must return an object or null', Constructor.displayName || 'ReactCompositeComponent') : _prodInvariant('82', Constructor.displayName || 'ReactCompositeComponent') : void 0;

      this.state = initialState;

到这里我们大概地晓得了一个组件从建立组织函数到实例化的时刻做了什么事情了。后续我们继承解读更底层的ReactComponent。

愿望能对人人有协助。
假如有毛病的处所,恳请列位大神斧正。

    原文作者:elliott_hu
    原文地址: https://segmentfault.com/a/1190000008098569
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞