React source code
ReactElement === React.createElement 返回的结果
例子:
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
ReactDOM.render(<App />, document.getElementById("root"));
render阶段
递 mount
采用深度优先遍历的方法,执行beginWork 方法
beginWork 流程
——
beginWork—— current -> `FilberRootNode` / `workInProgress ` -> `FilberRootNode`——
beginWork—— current ->
null/workInProgress->App/* 有子节点,继续向下递 */——
beginWork—— current ->
null/workInProgress->div/* 有子节点,继续向下递 */——
beginWork—— current ->
null/workInProgress->header/* 有子节点,继续向下递 */——
beginWork—— current ->
null/workInProgress->img/* 深度优先遍历,
img没有子节点,所以会走到归阶段 */——
completeWork—— current ->
null/workInProgress->img/* 发现有兄弟节点,进入兄弟节点 */——
- 根据双缓存机制,首次渲染的时候,
beginWork的 current 是指向根节点的,再次进入的时候,current是null, 其他节点只存于workInProgress
归 mount
completeWork->createElement->appendAllChildren->reconcilChildren
递 阶段 更新时候的流程 update
-
创建
workInProgress主要就是创建双缓存树
归 阶段 更新时候的流程 update
diff 算法
ReactChildFiber.old.js 文件下的 reconcileChildFibers 函数
多节点和单节点 Diff 本质: 比较 current fiber 与 jsx 返回的树。
单节点
首屏渲染:
- 首先判断是否存在对应DOM节点。
- 不存在,则直接
createFiberFromElement创建一个fiber树。 - 渲染到页面上。
更新渲染顺序:
- 上一次更新存在DOM节点,接下来判断是否可复用
- 首先比较
key是否相同 key相同,接下来比较type是否相同type相同,则表示可以复用, 返回复用的fiber: 代码中的方法为:useFibertype不同,则跳出当前判断。- 如果
key相同,但是type不同,将该fiber以及兄弟fiber标记为删除 - 如果
key不同 (对应第二步) 将该fiber标记为删除 - 最后,创建新
fiber并返回。
多节点
对于第一轮遍历的结果,我们分别讨论:
#newChildren与oldFiber同时遍历完
那就是最理想的情况:只需在第一轮遍历进行组件更新 (opens new window)。此时Diff结束。
#newChildren没遍历完,oldFiber遍历完
已有的DOM节点都复用了,这时还有新加入的节点,意味着本次更新有新节点插入,我们只需要遍历剩下的newChildren为生成的workInProgress fiber依次标记Placement。
你可以在这里 (opens new window)看到这段源码逻辑
#newChildren遍历完,oldFiber没遍历完
意味着本次更新比之前的节点数量少,有节点被删除了。所以需要遍历剩下的oldFiber,依次标记Deletion。
你可以在这里 (opens new window)看到这段源码逻辑
#newChildren与oldFiber都没遍历完
这意味着有节点在这次更新中改变了位置。
这是Diff算法最精髓也是最难懂的部分。我们接下来会重点讲解。
将剩余
oldFiber遍历生成一个map数据结构:key为keyfiber为value然后,将
newChildren中的key作为条件查找map中是否存在,有就复用,没有就新创建,打上标签,完成更新
update 链表 保存在 updateQueue 中