react-建立react元素

前言

react 元素,即JSX語法。react

const Nav, Profile;
// 輸入(JSX):
const app = <Nav color="blue"><Profile>click</Profile></Nav>;
// 通過react編譯解釋以後,輸出(JavaScript):
const app = React.createElement(
Nav,
{color:"blue"}, React.createElement(Profile, null, "click")
);

能夠看到,咱們平時在react 中寫的語法,最終是調用React的createElement方法。
那麼createElement作了什麼呢?
直接查看源碼,一探究竟。app

實現步驟

  • 獲取react 內部的 key , ref
  • 從props 上獲取self, source,和其餘屬性
  • 獲取children ,若是是有多個children ,須要freeze 住
  • 從type的defaultProps中填充缺失的屬性
  • 若是是開發環境,而且key 或 ref 存在,須要定義添加warning 信息
  • 返回一個ReactElement 對象

好,開始實現code

React.createElement = function(type, config, children) {
    var propName;
    var props = {};

    var key = null;
    var ref = null;
    var self = null;
    var source = null;

    if (config !== null) {

        if (hasValidKey(config)) {
            key = '' + config.key;
        }

        if (hasValidRef(config)) {
            ref = config.ref;
        }

        self = config.__self === undefined ? null : config.__self;
        source = config.__source === undefined ? null : config.__source;

        for(propName in config) {
            if (hasOwnerProperty.call(config, propName) && !REACT_RESERVED_PROPS.hasOwnProperty(propName) {
                props[propName] = config[propName];
            }
        }

        // 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 ];
            }
            if (__DEV__) {
                if (Object.freeze) {
                    Object.freeze(childArray);
                }
            }
            props.children = childArray;
        }

        // default Props
        if (type && type.defaultProps) {
            var defaultProps = type.defaultProps;
            for(propName in defaultProps) {
                if (props[propName] === undefined) {
                    props[propName] = defaultProps[propName];
                }
            }
        }

        // key || ref
        if (__DEV__) {
            if (key || ref) {
                if (typeOf config.$$typeOf === undefined || props.$$typeOf !== REACT_ELEMENT_TYPE) {
                    var displayName = typeOf type === 'function' ? type.displayName || type.name || 'Unknown'
                    : type;
                    if (key) {
                        defineKeyPropWarningGetter(props, displayName);
                    }
                    if (ref) {
                        defineRefPropWarningGetter(props, displayName);
                    }
                }
            }
        }

    } 

    return ReactElement(
            type,
            key,
            ref,
            self,
            source,
            ReactCurrentOwner.current,
            props
        );

};

在上述代碼中能夠看到,createElement 最終返回的是一個ReactElement 對象,那ReactElement又是什麼對象呢?
實現步驟:component

  • 設置react特殊標誌的屬性
  • 設置element 基本屬性
  • 開發環境下,添加適用於開發環境的屬性
ReactElement = function(type, key, ref, self, source, owner, props) {
    var element = {
        $$typeOf: REACT_ELEMENT_TYPE,
        type: type,
        key: key,
        ref: ref,
        // record the component responsible for creating the element
        _owner: owner,
    };

    if (__DEV__) {
        // self && source only dev props
        element._store = {};

        Object.defineProperty(element._store, 'validated', {
            writable: false,
            configurable: false,
            enumerable: false,
            value: false,
        });


        Object.defineProperty(element, '_self', {
            writable: false,
            configurable: false,
            enumerable: false,
            value: self,
        });

        Object.defineProperty(element, '_source', {
            writable: false,
            configurable: false,
            enumerable: false,
            value: source,
        });

        if (Object.freeze) {
            Object.freeze(element.props);
            Object.freeze(element);
        }

    }

    return element;
}

其中校驗key 和 self 是不是有效的方法爲:
(由於兩種是類似的,因此只列出一種)對象

var defineKeyPropWarningGetter = function(props, displayName) {
    var warnAboutAccessingKey = function() {
        if (!specialPropKeyWarningShown) {
            specialPropKeyWarningShow = true;
            warn(false, 
                '%s, `ref` prop 。。。。', displayName);
        }
    }

    warnAboutAccessingKey.isReactWarning = true;
    
    Object.defineProperty(props, 'key', {
        get: warnAboutAccessingKey
        configurable: true,
    })
};

var hasValidKey = function(config) {
    if (__DEV__) {
        if (hasOwnerProperty.call(config, 'key')) {
            var getter = Object.getOwnPropertyDescriptor(config, 'key').get;
            if (getter && getter.isReactWarning) {
                return false;
            }
        }
    }
    return config.key !== undefined;
}

其餘一些定義:ip

var REACT_ELEMENT_TYPE = (typeOf symbol && symbol.for && symbol.for('react.element')) || 0xeac7;
/**
 * Keeps track of the current owner.
 *
 * The current owner is the component who should own any components that are
 * currently being constructed.
 */
var ReactCurrentOwner = {
    current: (null: null | ReactInstance | Fiber)  // 這是什麼寫法???
}
var REACT_RESERVED_PROPS = {
    key: true,
    ref: true,
    __self: true,
    __source: true,
};

參考文獻:

react源碼:/16.0.0-rc.2/src/isomorphic/classic/element/ReactElement.js
陳屹:《深刻react技術棧》ci

相關文章
相關標籤/搜索