一、jsx变createElement
每一个用jsx语法书写的react组件最后都会变成 react.createElement(...)这一坨东西,
// 转变前export default (props)=>(
<h1 ref="h1" key="header1" name="我"><span>哈哈!</span>我是header.{props.kk}</h1>
);
// 转变后var _default = function _default(props) {
return _react2.default.createElement(
"h1",
{ ref: "h1", key: "header1", name: "\u6211" },
_react2.default.createElement(
"span",
null,
"\u54C8\u54C8\uFF01"
),
"\u6211\u662Fheader.",
props.kk
);
};
通过看代码就知道:header这个组件有三个子元素: span text 变量
可以说每遇到一个html标签就用createElement做包装,text 和 变量 不包装,直接按顺序当做参数传入createElement,有多少传多少
二、createElement拿到这些参数都干了啥
撸到createElement的源码块所在文件:
// react中createElement方法来源于 ReactElement.jsimport {
createElement,
createFactory,
cloneElement,
isValidElement,
} from './ReactElement';
找到createElement的源码:
/*** 传入了如下参数:
* type: "h1"
* config: { ref: "h1", key: "header1", name: "\u6211" }
* children: 1.react.createElement(...)
* 2.'我是header'
* 3. props.kk
*/
function createElement(type, config, children) {
// 一堆变量初始化
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
// 如果组件上存在属性设置,比如ref、key 其他props什么的
if (config != null) {
// 判断是否有ref属性且ref属性有值 单独取出来
if (hasValidRef(config)) {
ref = config.ref;
}
// 判断是否有key,单独取出来
if (hasValidKey(config)) {
key = '' + config.key;
}
// 先不管self 跟 source是干什么用的
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 把剩余的属性塞到props里面
for (propName in config) {
if (
/*
const hasOwnProperty = Object.prototype.hasOwnProperty;
const RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true,
};
*/
// 严谨的判断config对象中是否存在改属性,且属性名不能是react保留的那四种
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
// 放入props中
props[propName] = config[propName];
}
}
}
// 处理后面那些children
// 算出有几个children
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
// 如果就一个 直接赋值
props.children = children;
} else if (childrenLength > 1) {
// 整一个childArray 保存那些children
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
// dev环境不管丫的
if (__DEV__) {
....
}
// 最终还是塞到props里面
props.children = childArray;
}
// 如果type传的东西是个对象,且type有defaultProps这个东西,那就defaultProps的值也塞props里面
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
... //附加一堆开发环境才有的东西,先不去管它
}
// 最后返回ReactElement 函数执行后的返回值
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
我们注意到里面有一个 ReactCurrentOwner.current这个东西是个外来的,找到它:
const ReactCurrentOwner = {/**
* @internal
* @type {ReactComponent}
*/
current: (null: null | Fiber),
currentDispatcher: (null: null | Dispatcher),
};
// 实际上这个current初始时是null,类型可以是Fiber或null
其实绕来绕去,核心是 return ReactElement(...)这么一堆东西,就像剥洋葱,还得往下扒皮
三、ReactElement返回组件的真正形态
// 判断浏览器是否支持Symbolconst hasSymbol = typeof Symbol === 'function' && Symbol.for;
// 如果支持Symbol 则创建,否则用数字代替
export const REACT_ELEMENT_TYPE = hasSymbol
? Symbol.for('react.element')
: 0xeac7;
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE, // Symbol('react.element');
type: type, // h1
key: key, // header1
ref: ref, // h1
props: props, // {name:'\u6211',children:[...,...,...]}
_owner: owner, // null
};
if (__DEV__) {
...
}
return element;
};
这个element打印出来,其实它就是一个简简单单的对象
其他:
Symbol.for('abc') 和 Symbol('abc')有什么区别呢?
还没有评论,来说两句吧...