图解reactElement到fiber结构的转换

114 阅读4分钟

背景

上一篇博客我去了解了jsx是怎么转换为reactElement的过程,那么jsx转换后是怎么整合到完整的dom树中的呢,这就不得不提到reactElement转化为Fiber的过程了

转换逻辑

暂时无法在飞书文档外展示此内容

image.png

Fiber数据结构是React Fiber架构的核心部分,用于表示React中的每一个节点。它的作用可以总结如下:

  1. 节点类型

    1. tag:区分函数组件、类组件等不同类型的节点。
    2. elementTypetype:表示节点的类型,通常相同,但在开发环境下可能不同以支持热更新。
  2. 树结构

    1. return:指向父节点。
    2. child:指向第一个子节点。
    3. sibling:指向下一个兄弟节点。
    4. index:节点在兄弟节点中的位置。
  3. 引用

    1. refrefCleanup:用于处理组件上的引用。
  4. 属性和状态

    1. pendingProps:当前传入的props。
    2. memoizedProps:上一次渲染时的props。
    3. updateQueue:存储状态更新的队列。
    4. memoizedState:当前的状态。
  5. 依赖

    1. dependencies:节点的依赖关系。
  6. 模式

    1. mode:描述节点及其子树的属性,比如是否是并发模式。
  7. 副作用

    1. flagssubtreeFlags:描述节点及其子树的副作用。
    2. deletions:需要删除的子节点。
  8. 优先级

    1. laneschildLanes:表示节点及其子节点的优先级。
  9. 双缓存

    1. alternate:指向内存中的另一个Fiber节点,用于实现双缓存机制。

这个数据结构使得React能够高效地进行更新和渲染,支持复杂的UI变化和优化。

export type Fiber = {
  tag: WorkTag,  // 用于区分函数组件或者类组件
  key: null | string, // element元素的键值
  elementType: any,   // element转入的type值
  type: any,          // 一般与elementType相同,一些特殊情形下, 比如在开发环境下为了兼容热更新
  stateNode: any,     // 真实连接的dom元素
  // 链接部分
  return: Fiber | null,  // 父亲节点
  child: Fiber | null,   // 孩子节点
  sibling: Fiber | null, // 兄弟节点
  index: number,
  ref:
    | null
    | (((handle: mixed) => void) & {_stringRef: ?string, ...})
    | RefObject,        //指向在ReactElement组件上设置的 ref
  refCleanup: null | (() => void),  
  // 参数与记忆记录
  pendingProps: any, // // 从`ReactElement`对象传入的 props. 用于和`fiber.memoizedProps`比较可以得出属性是否变动
  memoizedProps: any, // 上一次生成子节点时用到的属性, 生成子节点之后保持在内存中
  updateQueue: mixed, // 存储state更新的队列, 当前节点的state改动之后, 都会创建一个update对象添加到这个队列中.
  memoizedState: any,  // 用于输出的state, 最终渲染所使用的state
  dependencies: Dependencies | null,
  mode: TypeOfMode,
  // Effect
  flags: Flags,
  subtreeFlags: Flags,
  deletions: Array<Fiber> | null,
 // 优先级相关
  lanes: Lanes,  // 本fiber节点的优先级
  childLanes: Lanes, // 子节点的优先级
  alternate: Fiber | null, // 双fiber缓存 指向内存中的另一个fiber, 每个被更新过fiber节点在内存中都是成对出现(current和workInProgress)
|};

  //...其它
};

对应构造函数 FiberNode

FiberNode函数的作用是创建一个新的Fiber节点实例。这个函数初始化了一个Fiber节点的所有属性。

function FiberNode(
  this: $FlowFixMe,
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;
  this.refCleanup = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // Effects
  this.flags = NoFlags;
  this.subtreeFlags = NoFlags;
  this.deletions = null;

  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  this.alternate = null;

   //...其它
}

函数的作用是根据传入的类型属性创建一个新的 Fiber 节点。具体来说,它会根据组件类型(如函数组件、类组件、原生组件等)和属性来创建相应的 Fiber 对象,并设置 Fiber 的各种属性以便 React 在协调和渲染过程中使用。

export function createFiberFromTypeAndProps(
  type: any, // React$ElementType
  key: null | string,
  pendingProps: any,
  owner: null | ReactComponentInfo | Fiber,
  mode: TypeOfMode,
  lanes: Lanes,
): Fiber {
  let fiberTag = FunctionComponent;
  // 根据传入的type进行fiberTag判断,基本分为函数型,对象,字符
  let resolvedType = type;
  if (typeof type === 'function') {
    if (shouldConstruct(type)) {
      fiberTag = ClassComponent;
      if (__DEV__) {
        resolvedType = resolveClassForHotReloading(resolvedType);
      }
    } else {
      if (__DEV__) {
        resolvedType = resolveFunctionForHotReloading(resolvedType);
      }
    }
  } else if (typeof type === 'string') {
    if (supportsResources && supportsSingletons) {
      const hostContext = getHostContext();
      fiberTag = isHostHoistableType(type, pendingProps, hostContext)
        ? HostHoistable
        : isHostSingletonType(type)
          ? HostSingleton
          : HostComponent;
    } else if (supportsResources) {
      const hostContext = getHostContext();
      fiberTag = isHostHoistableType(type, pendingProps, hostContext)
        ? HostHoistable
        : HostComponent;
    } else if (supportsSingletons) {
      fiberTag = isHostSingletonType(type) ? HostSingleton : HostComponent;
    } else {
      fiberTag = HostComponent;
    }
  } else {
    getTag: switch (type) {
      case ...  // 根据type的值进行归类判断
         return ...  // 调用不同类型的Fiber构造函数
      // Fall through
      default: {
        // ...对出现的异常状况做处理
      }
    }
  }

  const fiber = createFiber(fiberTag, pendingProps, key, mode);
  // 为新创建的fiber属性赋值
  fiber.elementType = type;
  fiber.type = resolvedType;
  fiber.lanes = lanes;

  return fiber;
}

createFiber的选择有enableObjectFiber决定,即是否允许直接使用对象(包含fiber创建需要的所有参数)创建fiber对象,否则则使用createFiberImplClass构造(即调用上文提到的FiberNode)新的fiber对象

const createFiber = enableObjectFiber
  ? createFiberImplObject
  : createFiberImplClass;

结语

哈哈哈,今天就到这里啦,或许后面会继续学习保持更新吧