From 45e5b1b24786de848d31146391f6c5d96596788f Mon Sep 17 00:00:00 2001 From: YuJian920 Date: Sat, 7 May 2022 22:13:14 +0800 Subject: [PATCH] vault backup: 2022-05-07 22:13:14 --- .../React 的流程解析 - Fiber 递归.md | 27 ++---------------- .../React 的深入探索 - beginWork.md | 28 +++++++++++++++++-- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/React 的源码深入/React 的流程解析 - Fiber 递归/React 的流程解析 - Fiber 递归.md b/React 的源码深入/React 的流程解析 - Fiber 递归/React 的流程解析 - Fiber 递归.md index 0c8216a..b3282da 100644 --- a/React 的源码深入/React 的流程解析 - Fiber 递归/React 的流程解析 - Fiber 递归.md +++ b/React 的源码深入/React 的流程解析 - Fiber 递归/React 的流程解析 - Fiber 递归.md @@ -3,36 +3,15 @@ 在 React 中,视图的更新使用了双缓存的方式,也就是说在 React 运行时,同时有着两棵 Fiber 树,一颗是当前视图上的 Fiber 树,叫 `current`,另外一棵是存在内存当中的下一次视图更新时用的叫做 `workInProgress`,React 在构建时会创建整个 app 唯一的根 Fiber 节点,叫做 `FiberRootNode`,这个节点上有一个 `current 指针`,指向的是当前正在页面上显示的 Fiber 树也就是 `current`,当 `workInProgress` 递归生成完毕,指针会立即指向 workInProgress ,而旧的 `current` 就会在下一次渲染中变成 `workInProgress` ,就这样循环交替完成页面的递归渲染 本次源代码解读的 DEMO 只用了最简单的 `useState` 做计数器修改触发页面刷新,并没有新增新的 DOM 节点之外的其他操作,第一次和第二次和之后页面刷新实际上都只是 `useState` 触发变化并在页面上显示计数内容出现变化 -# React 17.0.2 +# React 17 ## 首屏渲染 React 的首屏渲染时会交由 createWorkInProgress 函数生成 WorkInProgress 的第一个 Fiber 节点,这个节点就是 FiberNode,所以我们从 createWorkInProgress 函数开始讲解 React 中 Fiber 创建的流程,这个函数的主要工作就是根据传入的 Fiber 节点的 alternate 属性是否存在判断复用 Fiber 节点还是创建新的 Fiber 节点,具体的函数深入解析可以看 [[React 的深入探索 - createWorkInProgress]] -### beginWork - ->beginWork 执行在递归节点的 Fiber 创建之前,主要是为传入的 Fiber 节点根据类型创建第一个子 Fiber 节点 - -首先会对 current 做空值判断,因为对于首屏渲染而言,当前传入的 current Fiber 节点是 FiberNode,所以是存在的,会进入 current !== null 的逻辑,然后会判断当前 Fiber 节点是否发生变化,然后赋值 `didReceiveUpdate` 做更新标识,判断的条件如下: -1. 新旧 props 是否相同 -2. Context 是否发生变化 -3. Fiber 节点 type 是否发生变化 -在本次首屏渲染中,以上的条件都为否,会进入后续条件判断,在后续的条件判断中主要是为了应付一些特殊的场景,例如错误边界和 legacy 模式,可以看到官方留下的代码注释,在我们现在的场景下,最终会走到 didReceiveUpdate 为 false 的情况下,也就是当前 Fiber 并没有变化,因为在之前的 createWorkInProgress 中 WorkFiberProgress 被赋值上了 current Fiber 的同名属性,所以它们会是相同的两个 FIber 节点,在这里的代码可以看到:beginWork 中还有对优先值 lanes 的一些处理,在这里先不展开,后续再补充 -最后会走入 switch 环节,这了主要根据传入的 WorkInProgress Fiber 节点的类型进入不同的处理逻辑,这也是 beginWork 最主要的任务:为当前 WorkInProgress Fiber 节点创建它的第一个 Fiber 子节点,其内部根据 Fiber 的 tag 不同有对应的: -- updateClassComponent 处理 Class Component -- updateHostComponent 处理 Host Component -- updateSuspenseComponent 处理 Suspense Component -本次的是首屏渲染,传入的 WorkInProgress Fiber 为 FiberNode,所以会进入 HostRoot 的 case,执行 updateHostRoot 方法 - -### updateHostRoot - -updateHostRoot 函数中首先会执行 pushHostRootContext 方法,这个方法和 Context 相关,现在暂且不谈 -在 updateHostRoot 逻辑一开始,它会将 current 和 WorkInProgress 传递给 cloneUpdateQueue 这个函数,这个函数会将 Fiber 中的 queue 属性进行 clone,除非它们已经是 clone 之后的属性,那么具体是怎么做的判断是否是 clone 过的属性呢?其实很简单,它只是使用了 === 三个等于判断属性的引用是否相同,也就是说这个函数实际上做的是清除引用,保证在之后对 WorkInProgress 的操作不会影响到 current Fiber 树,判断两个 Fiber 树的 updateQueue 属性的引用完全相同,那么就会创建一个新的对象并重复赋值达到清除应用的目的,最后赋值给 WorkInProgress Fiber 节点 -之后,再把 WorkInProgress Fiber 节点交给 processUpdateQueue 函数,这个函数主要是处理更新队列,在这里先不展开,TODO -再往后是一些针对服务端渲染的一些处理逻辑,服务端渲染也不是这次讨论的目的,也先跳过 -最终调用 reconcileChildren 为 FIber 创建一个子 Fiber 节点并返回 +第一次 createWorkInProgress 执行完毕之后,接下来就会交给 beginWork 开始正式的 Fiber 递归,在 React 中 Fiber 的创建使用递归实现的深度优先遍历算法,即 -至此一个节点的 beginWork 流程就走完了,下一次会根据是否存在子 Fiber 节点判断是执行当前 WorkInProgress Fiber 节点的 completeWork ,还是继续对子节点执行 beginWork +beginWork 执行在递归节点的 Fiber 创建之前,主要是为传入的 Fiber 节点根据类型创建第一个子 Fiber 节点 ### completeWork diff --git a/React 的源码深入/React 的流程解析 - Fiber 递归/React 的深入探索 - beginWork.md b/React 的源码深入/React 的流程解析 - Fiber 递归/React 的深入探索 - beginWork.md index 7a5d72c..fa89ca3 100644 --- a/React 的源码深入/React 的流程解析 - Fiber 递归/React 的深入探索 - beginWork.md +++ b/React 的源码深入/React 的流程解析 - Fiber 递归/React 的深入探索 - beginWork.md @@ -7,8 +7,30 @@ 第一段是针对开发环境的逻辑,先跳过 在进入主要函数逻辑之前,会先进入一个对 current 的空值判断,这个 current 就是 React 双缓存机制中的 current Fiber 树的 Fiber 节点,然后进入 -对于首屏渲染而言这里传入的节点会是 FiberNode,也就是除了 FiberRootNode 外的第一个 Fiber 节点,具体可以看 [[React 的深入探索 - createWorkInProgress]] +对于首屏渲染而言这里传入的节点会是 FiberNode,也就是除了 FiberRootNode 外的第一个 Fiber 节点 -```javascript -``` \ No newline at end of file +### beginWork + +>beginWork 执行在递归节点的 Fiber 创建之前,主要是为传入的 Fiber 节点根据类型创建第一个子 Fiber 节点 + +首先会对 current 做空值判断,因为对于首屏渲染而言,当前传入的 current Fiber 节点是 FiberNode,所以是存在的,会进入 current !== null 的逻辑,然后会判断当前 Fiber 节点是否发生变化,然后赋值 `didReceiveUpdate` 做更新标识,判断的条件如下: +1. 新旧 props 是否相同 +2. Context 是否发生变化 +3. Fiber 节点 type 是否发生变化 +在本次首屏渲染中,以上的条件都为否,会进入后续条件判断,在后续的条件判断中主要是为了应付一些特殊的场景,例如错误边界和 legacy 模式,可以看到官方留下的代码注释,在我们现在的场景下,最终会走到 didReceiveUpdate 为 false 的情况下,也就是当前 Fiber 并没有变化,因为在之前的 createWorkInProgress 中 WorkFiberProgress 被赋值上了 current Fiber 的同名属性,所以它们会是相同的两个 FIber 节点,在这里的代码可以看到:beginWork 中还有对优先值 lanes 的一些处理,在这里先不展开,后续再补充 +最后会走入 switch 环节,这了主要根据传入的 WorkInProgress Fiber 节点的类型进入不同的处理逻辑,这也是 beginWork 最主要的任务:为当前 WorkInProgress Fiber 节点创建它的第一个 Fiber 子节点,其内部根据 Fiber 的 tag 不同有对应的: +- updateClassComponent 处理 Class Component +- updateHostComponent 处理 Host Component +- updateSuspenseComponent 处理 Suspense Component +本次的是首屏渲染,传入的 WorkInProgress Fiber 为 FiberNode,所以会进入 HostRoot 的 case,执行 updateHostRoot 方法 + +### updateHostRoot + +updateHostRoot 函数中首先会执行 pushHostRootContext 方法,这个方法和 Context 相关,现在暂且不谈 +在 updateHostRoot 逻辑一开始,它会将 current 和 WorkInProgress 传递给 cloneUpdateQueue 这个函数,这个函数会将 Fiber 中的 queue 属性进行 clone,除非它们已经是 clone 之后的属性,那么具体是怎么做的判断是否是 clone 过的属性呢?其实很简单,它只是使用了 === 三个等于判断属性的引用是否相同,也就是说这个函数实际上做的是清除引用,保证在之后对 WorkInProgress 的操作不会影响到 current Fiber 树,判断两个 Fiber 树的 updateQueue 属性的引用完全相同,那么就会创建一个新的对象并重复赋值达到清除应用的目的,最后赋值给 WorkInProgress Fiber 节点 +之后,再把 WorkInProgress Fiber 节点交给 processUpdateQueue 函数,这个函数主要是处理更新队列,在这里先不展开,TODO +再往后是一些针对服务端渲染的一些处理逻辑,服务端渲染也不是这次讨论的目的,也先跳过 +最终调用 reconcileChildren 为 FIber 创建一个子 Fiber 节点并返回 + +至此一个节点的 beginWork 流程就走完了,下一次会根据是否存在子 Fiber 节点判断是执行当前 WorkInProgress Fiber 节点的 completeWork ,还是继续对子节点执行 beginWork \ No newline at end of file