• react-创建react元素


    前言

    react 元素,即JSX语法。

    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做了什么呢?
    直接查看源码,一探究竟。

    实现步骤

    • 获取react 内部的 key , ref
    • 从props 上获取self, source,和其他属性
    • 获取children ,如果是有多个children ,需要freeze 住
    • 从type的defaultProps中填充缺失的属性
    • 如果是开发环境,并且key 或 ref 存在,需要定义添加warning 信息
    • 返回一个ReactElement 对象

    好,开始实现

    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又是什么对象呢?
    实现步骤:

    • 设置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;
    }
    

    其他一些定义:

    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技术栈》

  • 相关阅读:
    Codeforces Round #562题解
    Codeforces Round #561题解
    CF1107E Vasya and Binary String(区间dp)
    NC110113 Summer Earnings(bitset)
    NC112798 XOR-pyramid(dp)
    NC23051 华华和月月种树(离线+树状数组)
    py.path模块
    stat模块
    pwd模块
    PrettyTable模块
  • 原文地址:https://www.cnblogs.com/shixiaomiao1122/p/7501960.html
Copyright © 2020-2023  润新知