Render --- createElement

248 阅读3分钟

先写一个组件

    <!--
        首先是看一下,我们是用jsx的方式来写组件,jsx是语法糖方便我们来写React组件。而经过Babel转译之后的代码才是React本身需要用到的。
        
        其次我们来看看 App 组件是继承 (extends)了React.Component
        
    -->
    
    
    import React from "react";
    class App extends React.Component {
        render() {
            return <div>1233</div>;
        }
    }

看看 React.Component是什么

我们首先呀找到 React.Component。

先来看看 React 类。Component 是React的属性。

    <!--
        我们只看核心代码,看来React是合并了 ReactIsomorphic 的属性
    -->
    var React = {};
    assign(React, ReactIsomorphic);
    module.exports = React;
    

看看 ReactIsomorphic

    var React = {

        
        ...
        
        <!--
            同样的找重点,我们找到了React.Component是 ReactComponent。
        -->
        Component: ReactComponent,
        
        createElement: createElement,
        cloneElement: cloneElement,
        ...
    }

看看 ReactComponent

    <!--
        很简单,ReactComponent 是一个构造函数它有三个属性。
    -->
    function ReactComponent(props, context, updater) {
          this.props = props;
          this.context = context;
          this.refs = emptyObject;
        
          //  初始化一个 updater ,目前是默认值 (默认值啥也没干)。但是真正做事情的updater会被渲染器注入
          this.updater = updater || ReactNoopUpdateQueue;
    }
    
    <!--
        ReactComponent 的原型上定义了setState方法
    -->
    ReactComponent.prototype.setState = function (partialState, callback) {}
    <!--
        ReactComponent 的原型上定义了 forceUpdate 方法
    -->
    ReactComponent.prototype.forceUpdate = function (callback) {};
    

所有的React组件都是继承自 ReactComponent 的,所以React的组件都具有原型方法 setState 和三个属性。 setState 以后会说到,而三个属性会在接下来用到。

到此 我们知道了React的组件继承自ReactComponent,已经拥有基础的三个属性和几个原型方法。

而一个组件的核心方法是 render方法。即使一个组件什么都没有,也必须要求有一个render方法。

我们上边说过,我们目前使用jsx的写法来写React组件是语法糖,而一个 React组件的真面目如下:

你可以自己选择配置一下,babel编译React组件,看看其真面目,或者是这里有个小DEMO你可以拉下来看看 传送门

babel之后的 React组件

    <!--jsx语法的React组件-->
    
    import React from "react";
    class App extends React.Component {
          render() {
            return <div>1233</div>;
          }
    }
    
    <!--
    '
        babel转译后的 React组件,这里偷懒,React的版本是用了16.12.0,这样比较好配置babel。
    '
    -->
    
    var _react = _interopRequireDefault(require("react"));

    var App =
        function (_React$Component) {
      
        ... 省略不重要的代码
        <!--
        '
            这里会生成一个数组,数组内是我们子组件中定义的方法是以键值对来存在的。
        '
        -->
          (0, _createClass2["default"])(App, [{
            key: "render",-----------------------------(1)
            value: function render() {
              return _react["default"].createElement("div", null, "1233");
            }
          }]);
          return App;
    }(_react["default"].Component);
    
    <!--
    '
        如上,(1)中的key 就是我们在组件中写的方法,如果我们加入别的方法,比如生命周期 componentDidMount(),这里就会多一项。value值是我们定义的方法体。我们在组件中写的是 
        '
            render() {
                return <div>1233</div>;
            }
        '
        而实际上React处理的方式则是调用了。createElement 方法来处理 类html代码的。
    '
    -->
    

看一看 createElement 方法

ReactIsomorphic类中,我们很容易找到 createElement 方法

ReactElement 模块

    '
        核心是一个函数ReactElement,这个函数的作用是生成一个element返回
    '
    
    var ReactElement = function (type, key, ref, self, source, owner, props) {
      var element = {
       
        $$typeof: REACT_ELEMENT_TYPE,
    
       
        type: type,
        key: key,
        ref: ref,
        props: props,
    
        // Record the component responsible for creating this element.
        // 记录负责创建此 element 的 组件(component)
        _owner: owner
      };
      
      ...
      
      return element;
    };
    
    '
        模块还有一些方法,比如createElement方法就在这里。
    '
    ReactElement.createElement = function (type, config, children) {
    
    '
        参数:
        type:是HTML标签名称或者是React的组件名,比如 div,AppComponent等
        config: 是HTMl标签上的属性 className什么的
        children: HTML标签的子元素,比如要显示的 字符串
    '
    
      var propName;
    
     
    '
        提取名称并保留起来。
        主要是将config 中定义的属性 提取出来放置在 props中。
        当然,将一些特殊的取出来 也就是预留的属性 RESERVED_PROPS。  
    '
      var props = {};
    
      var key = null;
      var ref = null;
      var self = null;
      var source = null;
    
      if (config != null) {
        ref = config.ref === undefined ? null : config.ref;
        key = config.key === undefined ? null : '' + config.key;
        self = config.__self === undefined ? null : config.__self;
        source = config.__source === undefined ? null : config.__source;
        for (propName in config) {
          if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
            props[propName] = config[propName];
          }
        }
      }
    
    
    '
        Children,可能是一个或者是多个。将他们遍历一些并放在props的 children属性中
    '
      var childrenLength = arguments.length - 2;
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
      }
    
      
    '
        如果type是一个React组件比如App之类的。那么组件本身会有一个defautProps属性定义默认的属性值。 
        这里会遍历 defaultProps,如果 props属性中没有,则放进去,如果已经存在了,就跳过。
    '
    
      if (type && type.defaultProps) {
        var defaultProps = type.defaultProps;
        for (propName in defaultProps) {
          if (typeof props[propName] === 'undefined') {
            props[propName] = defaultProps[propName];
          }
        }
      }
    
      
    '
        ReactCurrentOwner.current 默认是null
    '
      
    '
        最后这里是调用了 ReactElement方法 我们前边说过,ReactElement方法最后会返回一个element.
    '
        return ReactElement(
            type, key, 
            ref, self, 
            source, ReactCurrentOwner.current, 
            props);
    };

简单的来说,React.createElement方法最后会返回一个element。

篇幅过长,不利于消化,我们另起一篇在来说说element.