TODO:
学习React源码的前提就是知道jsx,在react中并不是直接操作真实dom,而是通过jsx的语法,然后经过babel将jsx编译为ReactElement
所以最初的流程就是 jsx -> babel -> ReactElement。
以代码的形式看就如下
jsx
function App({age}) {
return (
<div key={1} className="container">
hello world
<p>I'm {age}</p>
</div>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(
<App age={26} />
);
经过babel转换后的代码
function App({
age
}) {
return React.createElement(
"div",
{
key: 1,
className: "container"
},
"hello world",
React.createElement("p", null, "I'm ", age)
);
}
ReactDOM.createRoot(document.getElementById('root')).render(
React.createElement(App, {
age: 26
})
);
React 在packages/react/src/ReactElement.js 中实现了createElement方法。
该方法会在运行时执行createElement(type, config, children)返回一个ReactElement,其结构如下:
export const REACT_ELEMENT_TYPE: symbol = Symbol.for('react.element');
// ...
const element = {
// This tag allows 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,
}
其中,比较重要的就是
- $$typeof : 唯一标识,表示是否为ReactElement,在后续的逻辑中会用到
- type : 表示的是当前的ReactElement类型,如果当前Element是一个宿主元素div,那么type就是’div’ string类型,如果是一个函数,那么久表示函数本身
- key : 这个属性就是在diff是优化用的
- props
- children : 子节点
- …其他的 : className、styles、ref …
到这里,对react的jsx、ReactElement就有了一定的了解了。不过,react作为一个前端框架,它和jquery直接操作DOM不同,它是基于状态驱动的。
jsx变化 -> ReactElement更新 -> reconciler、renderer -> 调用宿主API(DOM或其他平台) -> 显示真实DOM
可当前ReactElement这种结构
- 并不能表达节点与节点之间的关系,它只知道子节点
- 属性太少,无法表达节点的状态,比如一个函数组件需要和当前节点的上下文绑定,它就无法做到。
所以React 实现了一种新的结构FiberNode,这也是React中的虚拟DOM实现。它的特点如下:
介于
ReactElement和真实DOM之间:jsx变化 -> ReactElement更新 -> FiberNode -> reconciler、renderer -> 调用宿主API(DOM或其他平台) -> 显示真实DOM能够表达节点之间的关系
方便拓展(它既是数据存储单元,又是工作单元-> workLoop工作循环)
在下一节会讲解reconciler