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. Invalid bound statement (not found): com.shizongger.chapter2.mapper.UserMapper.insertUser 解决方案

    在配置MyBatis时报错信息如下: Invalid bound statement (not found): com.shizongger.chapter2.mapper.UserMapper.in ...

  2. Nginx 解决WebApi跨域二次请求以及Vue单页面问题

    一.前言 由于项目是前后端分离,API接口与Web前端 部署在不同站点当中,因此在前文当中WebApi Ajax 跨域请求解决方法(CORS实现)使用跨域处理方式处理而不用Jsonp的方式. 但是在一 ...

  3. sql基本知识

    1.类型转换 用convert,cast float转换出现科学计数字母的问题:可以先转成numeric再转成varchar declare @fl float set @fl=1234567.123 ...

  4. Java框架之Hibernate(二)

    本文主要介绍: 1 Criteria 接口 2 用 myeclipse 反向生成 3 hibernate  主键生成策略 4 多对一 5 一对多 6 使用List 集合的一对多 7 多对多 一.Cri ...

  5. Percona Toolkit 2.2.19 is now available

    New Features: 1221372: pt-online-schema-change now aborts with an error if the server is a slave, be ...

  6. centos6.8 docker0: iptables: No chain/target/match by that name

    现象:之前docker gitlab运行的一直好好的,突然有一天访问不了,但容器却还运行着.于是我把gitlab容器重启,发现启动不了了,报错:docker0: iptables: No chain/ ...

  7. js 抓取屏幕宽度设置字体大小 rem

    var doc = window.document var docEl = doc.documentElement var tid function refreshRem () { var width ...

  8. 关于java中,json字符串转集合和对象,或者集合转json字符串的解决方法

    1 List集合转成json字符串 //Java集合 List<String> list = new ArrayList<String>(); list.add("a ...

  9. 【Java】synchronized与lock的区别

    从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了 ...

  10. [Haskell] 为什么列表操作++很昂贵?

    博主是haskell新手.学习haskll的时候遇到了一些问题,在寻求答案的过程中产生了一些思考,可能理解存在偏差,希望各位不吝赐教. 提出问题 <Learn you a haskell for ...