|
|
|
# React 源码的碎片记录
|
|
|
|
- 每一个 Fiber 节点都会链接它的第一个子节点、下一个兄弟节点和父节点,并且在 Fiber 节点中还会保存组件的状态和需要更新的副作用
|
|
|
|
- Reconciler 会从根节点一直向下递到子节点,再从子节点一路向上归到根节点
|
|
|
|
- Fibet 中的 tag 属性保存了组件类型,如 Function Component、Class Component
|
|
|
|
- Fiber 中的 elementType 和 type 属性在大部分时候时相同,但是在 Function Component 使用React.memo 包裹时会有不同
|
|
|
|
- Fiber 中的 type 属性在使用 Function Component 时是函数本身,Class Component 时是 Class,Host Component 时是 DOM 节点的 Tag Name
|
|
|
|
- Fiber 中的 stateNode 属性对于 Host Component 而言,是它的真实 DOM 节点
|
|
|
|
- Fiber 中的 return、child、sibling 属性则对应链接Fiber链表中的父节点、子节点和兄弟节点
|
|
|
|
- Fiber 中的 index 属性代表在多个同级 Fiber 节点中,它们插入的位置索引
|
|
|
|
- Fiber 中带有 effcet 名称的属性代表与副作用相关,Host Component 中的 DOM 的增删改查,Functuon 中的 useEffect、useLayoutEffect
|
|
|
|
- Fiber 中的 lanes 属性和 childLanes 属性与 Fiber 的优先级的调度有关
|
|
|
|
- Fiber 中的 alternate 属性则表示了 Fiber 的工作方式
|
|
|
|
- React 的双缓存:React 有着 current Fiber 树和存在于内存中的 workInProgress Fiber 树
|
|
|
|
- React 的双缓存:每一次 React 的更新都会创建一个 workInProgress Fiber 树,current Fiber 和 workInProgress Fiber 之间使用 alternate 属性链接,方便公用属性,当 workInProgress Fiber 完成渲染,FiberRootNode 的指针就会指向 workInProgress Fiber 树的根节点 RootFiber,这时 workInProgress Fiber 就变成了 current Fiber 树
|
|
|
|
- React 会尽量的复用 Fiber,在创建 workInProgress Fiber 时,如果 current Fiber 中节点的 alternate 属性已经指向一个 Fiber 节点,那么新创建的 workInProgress Fiber 节点就会基于这个 alternate 指向的 Fiber 节点来创建,这种基于已有 Fiber 节点做对比生成新的 workInProgress Fiber 的过程就是 diff 算法,所以首屏渲染和更新渲染最大的区别就在于是否有 diff 算法
|
|
|
|
- 对于首屏渲染来说,只有根节点具有 current Fiber 而子节点不存在
|
|
|
|
- React render 阶段的工作分为归阶段和递阶段,在递阶段执行 beginWork,而在归阶段,执行 completeWork
|
|
|
|
- 如果 render 的递归阶段,如果节点只存在一个文本子节点,那么这个子节点将不会进入 beginWork 递阶段,而是由它父节点直接进入 completeWork 归阶段,这是 React 做的一个优化,而这一个逻辑在 updateHostComponent方法中的 isDirectTextChild 可以看到
|
|
|
|
- Host Components 会进入 updateHostComponent 逻辑,在 React 18 是 updateHostComponent$1
|
|
|
|
- updateHostComponent 方法中的 reconcileChildren 方法会为当前 Fiber 节点创建它的子 Fiber 节点,也就是 Fiber 中的 child 属性
|
|
|
|
- reconcileChildren 方法接受 current 参数,通过判断这个参数是否为 null,分别执行 mountChildFibers 或 reconcileChildFibers 方法
|
|
|
|
- mountChildFibers 和 reconcileChildFibers 都是由 ChildReconciler 方法创建的,只是传入的布尔值会不同,而这个参数表示是否追踪副作用,mountChildFibers 为 false,reconcileChildFibers则相反
|
|
|
|
- 以 reconcileChildFibers 为例,会对 Children 的类型做判断,对判断结果分别做相应操作
|
|
|
|
- completeWork 会对 beginWork 创建好的 Fiber 进行填充,根据 Fiber 类型的不同有不同的处理逻辑,其中有一步就是创建真实 DOM 元素并将之前创建好的 DOM 元素插入
|
|
|
|
- finalizeInitChildren 为创建的 DOM 元素,插入已有的 props,内部也根据 Fiber 节点的 tag 区分不同的处理逻辑,还有对 props 是否合法的校验,甚至根据 props 的属性也做了不同逻辑的处理,最终交由 setValueForProperty 处理
|
|
|
|
- completeWork 中的 appendAllChildren 会将创建好的真实DOM元素插入之前创建的子DOM元素
|
|
|
|
- 对于首屏渲染,只会有一个节点被打上 effectTag,就是根节点,只需要根节点被打上 effectTag 那么就能渲染剩下的全部内容
|
|
|
|
- createWorkInProgress 会创建新的 WorkInProgress Fiber,会根据 current Fiber 的 alternate 属性检查是否已经存在 WorkInProgress Fiber,如果没有会通过 createFiber 创建新的 Fiber 并将 current Fiber 的已有属性进行赋值,最后让 WorkInProgress Fiber 的 alternate 属性进行双向链接
|
|
|
|
- createWorkInProgress 之后就开始 Fiber 的递归创建,也就是 beginWork 和 completeWork,可以小小的归结,beginWork 是在 Fiber 递的过程中不断地创建下一个子 Fiber 节点,而 completeWork 则是填充创建好的 Fiber
|
|
|
|
- createWorkInProgress 的逻辑中,如果已经 alternate 存在,那么就会复用 Fiber,然后赋值同名参数,里边还会赋值 children
|
|
|
|
- createWorkInProgress -> beginWork -> completeWork
|
|
|
|
- 首屏渲染时,会创建 Fiber 树的第一个节点 FiberNode,这个时候创建的 Fiber 树会被 FiberRootNode 的 current 指针指向,之后进入 createWorkInProgress 函数,会从 current 树的 alternate 属性中获取 workInProgress 的 FiberNode,由于首屏渲染,这个时候只存在一棵树那就是 current Fiber,这时就会创建有个新的 Fiber 节点作为 workInProgress 的 FiberNode,并赋值 current FiberNode 的同名属性,最后交由 beginWork 开始创建子 Fiber 节点,直到 Fiber 树完成生成,提交给 commit 阶段渲染到页面上,当第一次触发页面刷新,也会进入 createWorkInProgress,这是存在一个完备的 Fiber 树,也就是首屏渲染时生成的 workInProgress,但是在第一次页面更新,它已经是此次的 current Fiber 树,与首屏渲染时的 createWorkInProgress 不同,这次的 current Fiber 树的 FiberNode 已经存在,那么就不会创建新的 Fiber 节点,而是复用已有的 FiberNode,并赋值上同名属性,最后再交由 beginWork生成子 Fiber 节点,最后逻辑依旧,完成 FIber 树的生成之后 commit 渲染出页面,直到第二次渲染,这时两棵树 current Fiber 树和 WorkInProgress 树都已经存在
|
|
|
|
- beginWork 在页面更新时,会根据一些条件判断 didReceiveUpdate 的 true 或者 false,这个变量代表了,在本次更新中这个 Fiber 节点是否有变化,这些条件分别是
|
|
|
|
1. 是否有新旧 props
|
|
|
|
2. context 是否发生变化
|
|
|
|
3. type 是否发生变化
|
|
|
|
- 如果条件都为否,那么 didReceiveUpdate 变成 false zh
|