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技术栈》
react-创建react元素的更多相关文章
- React 深入系列1:React 中的元素、组件、实例和节点
		
文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加深对React的理解,以及在项目中 ...
 - React + TypeScript:元素引用的传递
		
React 中需要操作元素时,可通过 findDOMNode() 或通过 createRef() 创建对元素的引用来实现.前者官方不推荐,所以这里讨论后者及其与 TypeScript 结合时如何工作. ...
 - react创建组件的几种方式及其区别
		
react创建组件有如下几种方式 ①.函数式定义的无状态组件 ②.es5原生方式React.createClass定义的组件 ③.es6形式的extends React.Component定义的组 ...
 - React创建组件的不同方式(ES5 & ES6)
		
一. 首先缕清楚React.createElement.React.createClass.React.Component之间的关系 1. React.createElement(HTML eleme ...
 - React创建组件的三种方式及其区别
		
内容转载于http://www.cnblogs.com/wonyun/p/5930333.html React推出后,出于不同的原因先后出现三种定义react组件的方式,殊途同归; 具体的三种方式: ...
 - React创建组件的方法,组件的props属性、state属性的用法和特点,父子组件传值,兄弟组件传值
		
创建组件的方法,组件的props属性.state属性的用法和特点,父子组件传值,兄弟组件传值 1.react组件 1.1.创建组件的方法 1.1.1.函数组件 定义一个组件最简单的方式是使用JavaS ...
 - 谈一谈创建React Component的几种方式
		
当我们谈起React的时候,多半会将注意力集中在组件之上,思考如何将页面划分成一个个组件,以及如何编写可复用的组件.但对于接触React不久,还没有真正用它做一个完整项目的人来说,理解如何创建一个组件 ...
 - React(一)使用脚手架创建React项目
		
1.安装脚手架 现在使用较多的就是这三种脚手架工具: react-boilerplate react-redux-starter-kit create-react-app 我使用的是第三种,faceb ...
 - react创建项目很慢,最后提示fetch failed的解决方法
		
$ cnpm install -g create-react-app //创建react全局变量 $ create-react-app my-app //创建一个react项目 国内使用 npm 速度 ...
 - 创建react项目的几种方法
		
前言: 构建React项目的几种方式: 构建:create-react-app 快速脚手架 构建:generator-react-webpack 构建:webpack一步一步构建 1)构建:creat ...
 
随机推荐
- vi 复制或剪切多行超级强大方法
			
同一个文件:光标移到起始行,输入ma 光标移到结束行,输入mb 光标移到粘贴行,输入mc 然后 :'a, 'b co 'c 把 co 改成 m 就成剪切了多个文件:在文件一: 光标移到起始行,输入ma ...
 - Centos7下安装与卸载docker应用容器引擎
			
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Li ...
 - 13LaTeX学习系列之---LaTeX插入表格
			
目录 目录 前言 (一)插入表格的基础语法 1.说明 2.源代码 3.输出效果 (二)查看文档 目录 本系列是有关LaTeX的学习系列,共计19篇,本章节是第13篇. 前一篇:12LaTeX学习系列之 ...
 - Kali下Ettercap 使用教程+DNS欺骗攻击
			
一.Ettercap 使用教程 EtterCap是一个基于ARP地址欺骗方式的网络嗅探工具.它具有动态连接嗅探.动态内容过滤和许多其他有趣的技巧.它支持对许多协议的主动和被动分析,并包含许多用于网络和 ...
 - 【Nginx】启动,重启,关闭命令
			
原文地址 https://github.com/zhongxia245/blog/issues/18欢迎 star nginx启动,重启,关闭命令 时间:2016-09-23 16:52:22 启动 ...
 - pb数据窗口之间的传参
			
问题描述: 通过一个窗口打开一个子窗口并传递指定参数查询详细信息 解决方法: 在前者窗体的user object下的itemchanged事件中,相应位置加入openwithparm函数 : op ...
 - 添加Nginx为系统服务(设置开机启动)
			
在本节中,我们将创建一个脚本,将Nginx守护进程转换为实际的系统服务. 这有两个作用:守护程序可以使用标准命令控制,更重要的是,它可以在系统启动时自动启动,并在系统关闭时停止. System V s ...
 - Django admin 的模仿流程
 - Linux操作系统中打开文件数量的查看方法
			
Linux操作系统中打开文件数量的查看方法ulimit -n 4096也就是限制用户的最大文件打开数为4096个 在网上查了关于怎么查看文件打开数的文章大致有两种说法/proc/sys/fs/file ...
 - day10,11练习
			
1.执行Python脚本两种方式? 略 2.简述位.字节关系? 8位一个字节 3.简述ASCII,Unicode,utf-8,gbk关系? ascii unicode utf8 4.请写出李杰分别用u ...