ReactNote03:JSX本质及源码解析
JSX 的本质
实际上,jsx 仅仅只是 React.createElement(component, props, …children) 函数的语法糖。所有的 jsx 语法都会被转换为 React.createElement 语法的调用。
先来看一下几行 jsx 代码:
<div>
<div>nihao</div>
<h2>you are the best</h2>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
那么它们转换成 js 代码会是什么样的呢?
React.createElement("div", null, React.createElement("div", null, "nihao"),
React.createElement("h2", null, "you are the best"),
React.createElement("ul", null,
React.createElement("li", null, "1"),
React.createElement("li", null, "2")))
好家伙,直接一个嵌套地狱,要是代码再多一点的话能把人看哭。还是 jsx 香
查看 React.createElement 的源码
源码位置:packages/react/src/ReactElement.js
/**
* Create and return a new ReactElement of the given type.
* See https://reactjs.org/docs/react-api.html#createelement
*/
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
if (__DEV__) {
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
key = '' + 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 (
hasOwnProperty.call(config, 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.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
const 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的三个参数:
- type
- 是当前 React 的类型,如果是标签元素,则用字符串表示,例如 “div”。如果是组件元素,则直接用组件名称
- config
- 所有 jsx 中的属性都在 config 中以对象的键值对存储
- 存放标签中的内容,以数组的方式存储
- 为什么是以数组的形式存储呢?
- 因为 jsx 里面可以会传入很多的同级标签,createElement 并不是只能传入三个参数。为什么可以传入多个参数呢?这就要回到源码里面一探究竟了,毕竟源码之下,了无秘密。
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
这里就是上面 createElement 源码的一部分,也就是说 children 是一个数组,里面存储着第三个参数及以后的所有参数。
------------- 本文结束 感谢阅读 -------------