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

posted @ 2017-09-10 20:31  shixiaomiao  阅读(876)  评论(0编辑  收藏  举报