antdソース解析(一)buttonコントロールの解析

27269 ワード

まずantdのbuttonのソースコードを見てみましょう.
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _extends2 = require('babel-runtime/helpers/extends');

var _extends3 = _interopRequireDefault(_extends2);

var _defineProperty2 = require('babel-runtime/helpers/defineProperty');

var _defineProperty3 = _interopRequireDefault(_defineProperty2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');

var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);

var _inherits2 = require('babel-runtime/helpers/inherits');

var _inherits3 = _interopRequireDefault(_inherits2);

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

var _classnames = require('classnames');

var _classnames2 = _interopRequireDefault(_classnames);

var _omit = require('omit.js');

var _omit2 = _interopRequireDefault(_omit);

var _icon = require('../icon');

var _icon2 = _interopRequireDefault(_icon);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : {'default': obj};
}

var __rest = undefined && undefined.__rest || function (s, e) {
    var t = {};
    for (var p in s) {
      if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
    }
    if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
      if (e.indexOf(p[i]) < 0) t[p[i]] = s[p[i]];
    }
    return t;
  };

var rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
var isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
function isString(str) {
  return typeof str === 'string';
}
// Insert one space between two chinese characters automatically.
function insertSpace(child, needInserted) {
  // Check the child if is undefined or null.
  if (child == null) {
    return;
  }
  var SPACE = needInserted ? ' ' : '';
  // strictNullChecks oops.
  if (typeof child !== 'string' && typeof child !== 'number' && isString(child.type) && isTwoCNChar(child.props.children)) {
    return _react2['default'].cloneElement(child, {}, child.props.children.split('').join(SPACE));
  }
  if (typeof child === 'string') {
    if (isTwoCNChar(child)) {
      child = child.split('').join(SPACE);
    }
    return _react2['default'].createElement(
      'span',
      null,
      child
    );
  }
  return child;
}

var Button = function (_React$Component) {
  (0, _inherits3['default'])(Button, _React$Component);

  function Button(props) {
    (0, _classCallCheck3['default'])(this, Button);

    var _this = (0, _possibleConstructorReturn3['default'])(this, (Button.__proto__ || Object.getPrototypeOf(Button)).call(this, props));

    _this.handleClick = function (e) {
      // Add click effect
      _this.setState({clicked: true});
      clearTimeout(_this.timeout);
      _this.timeout = setTimeout(function () {
        return _this.setState({clicked: false});
      }, 500);
      var onClick = _this.props.onClick;
      if (onClick) {
        onClick(e);
      }
    };
    // Handle auto focus when click button in Chrome
    _this.handleMouseUp = function (e) {
      if (_this.props.onMouseUp) {
        _this.props.onMouseUp(e);
      }
    };
    _this.state = {
      loading: props.loading
    };
    return _this;
  }

  (0, _createClass3['default'])(Button, [
    {
      key: 'componentWillReceiveProps',
      value: function componentWillReceiveProps(nextProps) {
        var _this2 = this;

        var currentLoading = this.props.loading;
        var loading = nextProps.loading;
        if (currentLoading) {
          clearTimeout(this.delayTimeout);
        }
        if (typeof loading !== 'boolean' && loading && loading.delay) {
          this.delayTimeout = setTimeout(function () {
            return _this2.setState({loading: loading});
          }, loading.delay);
        } else {
          this.setState({loading: loading});
        }
      }
    },
    {
      key: 'componentWillUnmount',
      value: function componentWillUnmount() {
        if (this.timeout) {
          clearTimeout(this.timeout);
        }
        if (this.delayTimeout) {
          clearTimeout(this.delayTimeout);
        }
      }
    },
    {
      key: 'render',
      value: function render() {
        var _classNames;

        var _a = this.props,
          type = _a.type,
          shape = _a.shape,
          _a$size = _a.size,
          size = _a$size === undefined ? '' : _a$size,
          className = _a.className,
          htmlType = _a.htmlType,
          children = _a.children,
          icon = _a.icon,
          prefixCls = _a.prefixCls,
          ghost = _a.ghost,
          others = __rest(_a, ["type", "shape", "size", "className", "htmlType", "children", "icon", "prefixCls", "ghost"]);
        var _state = this.state,
          loading = _state.loading,
          clicked = _state.clicked;
        // large => lg
        // small => sm

        var sizeCls = '';
        switch (size) {
          case 'large':
            sizeCls = 'lg';
            break;
          case 'small':
            sizeCls = 'sm';
          default:
            break;
        }
        var classes = (0, _classnames2['default'])(prefixCls, className, (_classNames = {}, (0, _defineProperty3['default'])(_classNames, prefixCls + '-' + type, type), (0, _defineProperty3['default'])(_classNames, prefixCls + '-' + shape, shape), (0, _defineProperty3['default'])(_classNames, prefixCls + '-' + sizeCls, sizeCls), (0, _defineProperty3['default'])(_classNames, prefixCls + '-icon-only', !children && icon && !loading), (0, _defineProperty3['default'])(_classNames, prefixCls + '-loading', loading), (0, _defineProperty3['default'])(_classNames, prefixCls + '-clicked', clicked), (0, _defineProperty3['default'])(_classNames, prefixCls + '-background-ghost', ghost), _classNames));
        var iconType = loading ? 'loading' : icon;
        var iconNode = iconType ? _react2['default'].createElement(_icon2['default'], {type: iconType}) : null;
        var needInserted = _react2['default'].Children.count(children) === 1 && !iconType;
        var kids = _react2['default'].Children.map(children, function (child) {
          return insertSpace(child, needInserted);
        });
        return _react2['default'].createElement(
          'button',
          (0, _extends3['default'])({}, (0, _omit2['default'])(others, ['loading', 'clicked']), {
            type: htmlType || 'button',
            className: classes,
            onMouseUp: this.handleMouseUp,
            onClick: this.handleClick
          }),
          iconNode,
          kids
        );
      }
    }
  ]);
  return Button;
}(_react2['default'].Component);

exports['default'] = Button;

Button.__ANT_BUTTON = true;
Button.defaultProps = {
  prefixCls: 'ant-btn',
  loading: false,
  clicked: false,
  ghost: false
};
Button.propTypes = {
  type: _propTypes2['default'].string,
  shape: _propTypes2['default'].oneOf(['circle', 'circle-outline']),
  size: _propTypes2['default'].oneOf(['large', 'default', 'small']),
  htmlType: _propTypes2['default'].oneOf(['submit', 'button', 'reset']),
  onClick: _propTypes2['default'].func,
  loading: _propTypes2['default'].oneOfType([_propTypes2['default'].bool, _propTypes2['default'].object]),
  className: _propTypes2['default'].string,
  icon: _propTypes2['default'].string
};
module.exports = exports['default'];

ボタンコントロールがこんなに多くのコードを持っているのを見て、びっくりしました.コードを簡略化して、実装に必要なコードを見てみましょう.
import  React,{ Component }  from "React";

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : {'default': obj};
}

var _createClass2 = require('babel-runtime/helpers/createClass');
var _inherits2 = require('babel-runtime/helpers/inherits');
// 
var Button = function (_React$Component) {
//    
  _inherits2['default'](Button, _React$Component);
  console.log("test-----------");
  function Button(props) {

  }
  //render   。
 _createClass2['default'](Button, [ 
    {
      key: 'render',
      value: function render() {
        return 
} } ]); return Button; }(React.Component); export default Button;

コード解析:
   (0, _extends3['default'])({}, (0, _omit2['default'])(others, ['loading', 'clicked']), {
            type: htmlType || 'button',
            className: classes,
            onMouseUp: this.handleMouseUp,
            onClick: this.handleClick
          }),

実は等価
_extends3['default']({}, (0, _omit2['default'])(others, ['loading', 'clicked']), {
            type: htmlType || 'button',
            className: classes,
            onMouseUp: this.handleMouseUp,
            onClick: this.handleClick
          })

コンポーネント定義時に次の文が呼び出されます.少なくはありません.
_inherits2['default'](Button, _React$Component);

実はこの関数が呼び出されました

exports.default = function (subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === "undefined" ? "undefined" : (0, _typeof3.default)(superClass)));
  }

  subClass.prototype = (0, _create2.default)(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
  if (superClass) _setPrototypeOf2.default ? (0, _setPrototypeOf2.default)(subClass, superClass) : subClass.__proto__ = superClass;
};

このコードが読めない場合、Buttonオブジェクトが定義され、そのオブジェクトがReactを継承すると理解できる.Component定義に関するメソッドの説明.実は次のようなものです.
var Button = class Button extends Component{
   ...
};

_createClass 2のソースコードは次のとおりです.
exports.default = function () {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      (0, _defineProperty2.default)(target, descriptor.key, descriptor);
    }
  }

  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();

コア文.
    (0, _defineProperty2.default)(target, descriptor.key, descriptor);

実はrenderメソッドを定義しており、次のコードの効果と一致しています.
render(){
    return  <div>
          <button > button>
        div>
}

中の具体的な細い点は下節に更に分析します