书籍传送门:React技术揭秘——卡颂

前言:整篇文章,我都将以问题+笔记的形式作为精读的导语展开叙述~文末有彩蛋喔~~~

上一篇文章中,我们已经了解了fiber架构,fiber思想以及React fiber的设计理念,本篇,我们着重了解架构篇

React 架构篇

分为Render&Commit两个阶段;

Render 阶段做了什么事?

Render阶段可以简单描述为:先遍历—>后递归。遍历diff、递归build dom

Render阶段细分为:beginWork & completeWork

  • beginWork
    • 传入当前 fiber节点,创建子fiber节点
      function beginWork(
         current: Fiber | null,
         workInProgress: Fiber,
         renderLanes: Lanes,
      ): Fiber | null {
         // ...省略函数体
      }
    • current指的是wip.alternate,也就是当前组件上一次更新时的fiber节点;
    • workInProgress指的是当前组件的fiber;
    • renderLanes指的是优先级

可以通过current !== null来区分是mount还是update,基于此,beginWork又可以分为两个阶段:mount&update.

mount阶段,会根据不同的fiber.tag创建不同的fiber节点。

update阶段

  • 会根据propsfiber.type的比对决定是否要复用fiber节点
  • 根据renderLanes判断优先级,配合Schedule去实现任务调度

问题:如果不能复用,怎么办呢?

答:进入diff,交给reconcile去处理。

beginWork结束后,就会进入reconcileChildren.也就是传说中的diff算法

注意

值得一提的是,mountChildFibers 与 reconcileChildFibers 这两个方法的逻辑基本一致。唯一的区别是:reconcileChildFibers 会为生成的 Fiber 节点带上 effectTag 属性,而 mountChildFibers 不会。

reconcileChildren结束后,紧接着就是根据diff结果为fiber打上effetTag,例如:它是增、改、还是删等。

// DOM需要插入到页面中
export const Placement = /*                */ 0b00000000000010;
// DOM需要更新
export const Update = /*                   */ 0b00000000000100;
// DOM需要插入到页面中并更新
export const PlacementAndUpdate = /*       */ 0b00000000000110;
// DOM需要删除
export const Deletion = /*                 */ 0b00000000001000;

小疑问:mount阶段也会打Placementtag,那么执行 插入操作会不会很低效?

答:mount阶段确实会打Placement tag,但是是在rootFiber上打的。

  • completeWork

beginWork一样,都是根据不同的tag去做不同的处理。

update

  • 处理事件相关的回调函数的注册
  • 处理 style prop
  • 处理DANGEROUSLY_SET_INNER_HTML prop
  • 处理children prop

mount

删除的文字是卡老师原文中的,但是,我觉得只要没渲染到页面上,它就还是Fiber树~正好跟上缓存的概念相对应。

  • Fiber节点生成对应的DOM节点Current Fiber[屏幕上显示的]节点生成对应的WIP Fiber[正在内存中构建的]节点。
  • 将子孙DOM节点插入刚生成的DOM节点中 将子孙Fiber节点插入刚生成的Fiber节点中。
  • update相似的的处理props

文末彩蛋:手写笔记