ReactElement算是React源码中比较简单的部分了,直接看源码:

var ReactElement = function(type, key, ref, self, source, owner, props) {
var element = {
// This tag allow us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props, // Record the component responsible for creating this element.
_owner: owner,
}; if (__DEV__) {
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {}; // To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
if (canDefineProperty) {
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
// self and source are DEV only properties.
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
} else {
element._store.validated = false;
element._self = self;
element._source = source;
}
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
} return element;
};

可以看出ReactElement就是一个函数,传入一系列参数作为一个element对象的属性,然后再把这个对象return出来,但是注意到有一个属性是$$typeof: REACT_ELEMENT_TYPE,然后我查找了一下这个REACT_ELEMENT_TYPE的定义:

// The Symbol used to tag the ReactElement type. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
var REACT_ELEMENT_TYPE =
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
0xeac7;

根据注释可以看出这个属性是用来标记当前对象是一个ReactElement,如果不是原生Symbol类型或者填充,则使用普通数字来代替,这也区分了element并非普通对象,而是特殊的ReactElement。

接下来我们再来看一下ReactElement.createElement这个方法,还是直接来看源码:

ReactElement.createElement = function(type, config, children) {
var propName; // Reserved names are extracted
var props = {}; var key = null;
var ref = null;
var self = null;
var source = null; if (config != null) {
if (__DEV__) {
ref = !config.hasOwnProperty('ref') ||
Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
key = !config.hasOwnProperty('key') ||
Object.getOwnPropertyDescriptor(config, 'key').get ? null : '' + config.key;
} else {
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;
// Remaining properties are added to a new props object
for (propName in config) {
if (config.hasOwnProperty(propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
} // Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
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;
} // Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
// Create dummy `key` and `ref` property to `props` to warn users
// against its use
if (typeof props.$$typeof === 'undefined' ||
props.$$typeof !== REACT_ELEMENT_TYPE) {
if (!props.hasOwnProperty('key')) {
Object.defineProperty(props, 'key', {
get: function() {
if (!specialPropKeyWarningShown) {
specialPropKeyWarningShown = true;
warning(
false,
'%s: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
typeof type === 'function' && 'displayName' in type ? type.displayName : 'Element'
);
}
return undefined;
},
configurable: true,
});
}
if (!props.hasOwnProperty('ref')) {
Object.defineProperty(props, 'ref', {
get: function() {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
warning(
false,
'%s: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
typeof type === 'function' && 'displayName' in type ? type.displayName : 'Element'
);
}
return undefined;
},
configurable: true,
});
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props
);
};

有点长,但其实也很简单,总结一下就是四点:

1,如果有config参数,那么就把config内的同名ReactElement属性给替换掉;

2,如果arguments.length为3,表示有第三个参数children,并且children只有一个,那么直接进行赋值同名属性children,如果大于3,表示children不止一个,就直接for循环赋值;

3,如果存在type参数并且有defaultprops静态变量,那么就循环defaultProps的属性,只要属性名不为undefined,就赋值给props同名属性;

4,不允许向props添加key和ref属性;

最后返回创建的ReactElement。

还有一个常用的是React.cloneElement:

ReactElement.cloneElement = function(element, config, children) {
var propName; // Original props are copied
var props = Object.assign({}, element.props); // Reserved names are extracted
var key = element.key;
var ref = element.ref;
// Self is preserved since the owner is preserved.
var self = element._self;
// Source is preserved since cloneElement is unlikely to be targeted by a
// transpiler, and the original source is probably a better indicator of the
// true owner.
var source = element._source; // Owner will be preserved, unless ref is overridden
var owner = element._owner; if (config != null) {
if (config.ref !== undefined) {
// Silently steal the ref from the parent.
ref = config.ref;
owner = ReactCurrentOwner.current;
}
if (config.key !== undefined) {
key = '' + config.key;
}
// Remaining properties override existing props
var defaultProps;
if (element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
for (propName in config) {
if (config.hasOwnProperty(propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)) {
if (config[propName] === undefined && defaultProps !== undefined) {
// Resolve default props
props[propName] = defaultProps[propName];
} else {
props[propName] = config[propName];
}
}
}
} // Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
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;
} return ReactElement(
element.type,
key,
ref,
self,
source,
owner,
props
);
};

如果config里面有ref,key,props,则全部替换掉config里面的,同名的props直接覆盖,同时参数的children,也作为新的children,最后返回赋值后的ReactElement;

最后还有一个检测ReactElement是否为有效的方法ReactElement.isValidElement:

/**
* @param {?object} object
* @return {boolean} True if `object` is a valid component.
* @final
*/
ReactElement.isValidElement = function(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
};

而这个REACT_ELEMENT_TYPE前面已经介绍过了,这里不再多说。

1

React源码解析:ReactElement的更多相关文章

  1. React源码解析之React.Children.map()(五)

    一,React.Children是什么? 是为了处理this.props.children(this.props.children表示所有组件的子节点)这个属性提供的工具,是顶层的api之一 二,Re ...

  2. React源码解析——ReactAPI

    一.API背景 api的具体转化关系 可以通过到https://babeljs.io/repl/网站去将我们创建的Jsx进行实时的转译 const React = { Children: { map, ...

  3. React源码解析:setState

    先来几个例子热热身: ......... constructor(props){ super(props); this.state = { index: 0 } } componentDidMount ...

  4. React源码解析-Virtual DOM解析

    前言:最近一直在研究React,看了陈屹先生所著的深入React技术栈,以及自己使用了这么长时间.对React应该说有比较深的理解了,正好前阵子也把两本关于前端设计模式的书看完了,总感觉有一种知识错综 ...

  5. React源码解析——创建更新过程

    一.ReactDOM.render 创建ReactRoot,并且根据情况调用root.legacy_renderSubtreeIntoContainer或者root.render,前者是遗留的 API ...

  6. React躬行记(16)——React源码分析

    React可大致分为三部分:Core.Reconciler和Renderer,在阅读源码之前,首先需要搭建测试环境,为了方便起见,本文直接采用了网友搭建好的环境,React版本是16.8.6,与最新版 ...

  7. React的React.createRef()/forwardRef()源码解析(三)

    1.refs三种使用用法 1.字符串 1.1 dom节点上使用 获取真实的dom节点 //使用步骤: 1. <input ref="stringRef" /> 2. t ...

  8. React的React.createElement源码解析(一)

    一.什么是jsx  jsx是语法糖  它是js和html的组合使用  二.为什么用jsx语法 高效定义模版,编译后使用 不会带来性能问题 三.jsx语法转化为js语法  jsx语法通过babel转化为 ...

  9. React的React.createContext()源码解析(四)

    一.产生context原因 从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便.简介. 二.context的两种实现方式 1.老版本(React16.x ...

随机推荐

  1. python的defaultdict

    defaultdict是dict的一个子类,接受一个工厂函数作为参数,当访问defaultdict中不存在的key时,会将工厂函数的返回值作为默认的value. class defaultdict(d ...

  2. js 数组API之every、some用法

    every 判断数组中是否每个元素都满足条件 只有都满足条件才返回true: 只要有一个不满足就返回false: arr.every(function(value,index,array){retur ...

  3. 在阿里云 ECS 搭建 nginx https nodejs 环境(三、nodejs)

    在阿里云 ECS 搭建 nginx https nodejs 环境(三.nodejs) 在阿里云 ECS 搭建 nginx https nodejs 环境(三.nodejs) 第一步 指定版本源 执行 ...

  4. Android异常分析(转)

    关于异常 异常? 异常就是一种程序中没有预料到的问题,既然是没有预料到的,就可能不在原有逻辑处理范围内,脱离了代码控制,软件可能会出现各种奇怪的现象.比如:android系统常见异常现象有应用无响应. ...

  5. C# war3 巨魔精灵 minimap

    弃坑LOL后,无聊的时候玩玩 war3的RPG地图,巨魔与精灵.  玩了一段时间精灵....然后玩魔结果总是找不到人.所以就有了这个想法. 代码纯粹靠搬运. 说下原理,网上有份代码,可以查看当前选中目 ...

  6. asp.net mvc 防止重复提交

    string hiddenToken = "hiddenToken"; public void OnActionExecuting(ActionExecutingContext f ...

  7. Apache kafka 工作原理介绍

    消息队列 消息队列技术是分布式应用间交换信息的一种技术.消息队列可驻留在内存或磁盘上, 队列存储消息直到它们被应用程序读走.通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置.或在继续执行 ...

  8. Watch time

    Stopwatch watch = new Stopwatch(); watch.Start(); ; i <= ; i++) { ; j <= ; j++) { ; k <= ; ...

  9. swiper插件的简单使用,实现图片轮播

    移动端和p c端经常会遇到写轮播图的情况,这里只是简单的说一下swiper插件的简单用法(移动端为例). <!DOCTYPE html> <html lang="en&qu ...

  10. uImage和zImage的区别

    1.各种文件的意义 vmlinux  编译出来的最原始的内核文件,未压缩. zImage   是vmlinux经过gzip压缩后的文件. bzImage bz表示“big zImage”,不是用bzi ...