import React, { Component } from "react";
import ReactDOM from "react-dom";
let jsx = (
<div className="box border">
<p className="p-bb border">这是一个jsx</p>
</div>
);
ReactDOM.render(jsx, document.getElementById("root"));
如上是一个最基础的render过程,将创建好的jsx内容挂在到id为root的DOM上
在这个过程中执行了什么呢?代码中我们也没有看到对React的引用,但它却是必须的
大家都知道react框架有虚拟dom的定义,这里的步骤大概有
- babel-loader对jsx进行编译
- 通过React.createElement函数按照一定逻辑生成成携带标签及其字元素信息的vnode
- 这个vnode则将作为render函数的真正入参,生成真实的node
- 生成的node挂载到document.getElementById("root")指向的元素上
口说无凭,咱们看源码中的API
version: react@16.11.0
createElement
返回一个更方便react框架(ReactElement)解析的对象
代码位置 packages\react\src\ReactElement.js
/**
* type 组件类型(原生标签名称、函数组件、类组件)
*
*/
export function createElement(type, config, children) {
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];
}
}
}
}
此处函数内部
- 将config结构到函数的props中
- 将arguments.length - 2 后的所有参数当作子组件放入props.children
- 将type.defaultProps解构到props中
类似处理函数还有cloneElement
export function cloneElement(element, config, children) {
if (element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
return ReactElement(element.type, key, ref, self, source, owner, props);
}
- 根据一个已有的element通过Object.assign({}, element.props)获取基础的pross
- 通过结构config扩充props
render
render是reactDom的核心函数
代码位置:packages\react-dom\src\client\ReactDOMLegacy.js
export function render(
element: React$Element<any>,
container: Container,
callback: ?Function,
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
根据上面的逻辑下节我们可以手动模拟实现jsx=>vnode=>node=>render的一个过程