diff --git a/自顶向下学 React 源码/React 源码的碎片记录.md b/自顶向下学 React 源码/React 源码的碎片记录.md index 449707d..40f0359 100644 --- a/自顶向下学 React 源码/React 源码的碎片记录.md +++ b/自顶向下学 React 源码/React 源码的碎片记录.md @@ -1,10 +1,7 @@ - 每一个 Fiber 节点都会链接它的第一个子节点、下一个兄弟节点和父节点,并且在 Fiber 节点中还会保存组件的状态和需要更新的副作用 -- Reconciler 会从根节点一直向下递到子节点,再从子节点一路向上归到根节点 -- 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 可以看到 - updateHostComponent 方法中的 reconcileChildren 方法会为当前 Fiber 节点创建它的子 Fiber 节点,也就是 Fiber 中的 child 属性 - reconcileChildren 方法接受 current 参数,通过判断这个参数是否为 null,分别执行 mountChildFibers 或 reconcileChildFibers 方法 @@ -14,10 +11,6 @@ - finalizeInitialChildren 为创建的 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