React Fiber 类型的结构

805 阅读3分钟

Fiber

需要注意的地方:

  • 组件第一次运行时为 fibet mount阶段;后续更新为 fiber update阶段。
  • 函数组件 mountfibet.tag 为 IndeterminateComponent 。
    • 历史遗留问题,旧版本支持 函数工厂 组件,函数返回一个有 render 函数的对象,会被当做 ClassComponent 处理。 此时 tag 赋重新赋值为 ClassCompoent 否则赋值为 FunctionComponent 
  • key 会强制转换 string 类型。
    • key = '' + config.key; 
export type Fiber = {|
  // 类型 eg: 0:FunctionComponent,1:ClassComponent,2:IndeterminateComponent
  tag: WorkTag,
  // fiber key
  key: null | string,

  // diff中保存type
  elementType: any,

  // fiber的类型 host/function/class/  eg: 'div'; function FC(); class C extend Component;
  type: any,

  // 宿主实例。 浏览器中为DOM
  stateNode: any,

	// fiber父节点
  return: Fiber | null,

  // 第一个孩子节点 
  child: Fiber | null,
  // 兄弟节点                   
  sibling: Fiber | null,
  // fiber是依据jsx生成的。 
  // 此fiber对应的jsx,在jsxChildren中的位置索引
  // eg:<App><input/><textarea/></App>    textareaFiber的index为1
  index: number,

  // ref引用
  ref:
    | null
    | (((handle: mixed) => void) & {_stringRef: ?string, ...})
    | RefObject,

  pendingProps: any, // 渲染中正在计算的props
  memoizedProps: any, // 实际使用的props。也可以认为是上一次渲染的props缓存值。

  // 更新队列 类组件处理state;函数组件是hooks effect;原生(DOM)组件是属性变化;
  updateQueue: mixed,

  // 实际使用的state。也可以认为是上一次渲染的state缓存值。
  memoizedState: any,

  // 所依赖的context,event
  dependencies: Dependencies | null,

  // 模式  eg: ConcurrentMode ; StrictLegacyMode
  mode: TypeOfMode,

  // Effect fiber的副作用
  // 二进制补码。 来标记fiber的effect  eg:Update Placment Deletion Ref 相关Hooks
  flags: Flags,
  // 子树的effect集合,用于性能优化。eg:为NoFlags直接跳过子树
  subtreeFlags: Flags,
  // 需要删除的child
  deletions: Array<Fiber> | null,
  
  // 已被废弃;
  // commit阶段通过effectList来进行性能优化
  nextEffect: Fiber | null,
  firstEffect: Fiber | null,
  lastEffect: Fiber | null,

  // 优先级                                    
  lanes: Lanes,
  // 子树优先级                                    
  childLanes: Lanes,

  // 备用; newFiber与oldFiber进行切换;
  alternate: Fiber | null,
                                    
	// Profiler使用;记录更新时长
  actualDuration?: number,
  actualStartTime?: number,
  selfBaseDuration?: number,
  treeBaseDuration?: number,
  // dev debug
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
  _debugNeedsRemount?: boolean,
  _debugHookTypes?: Array<HookType> | null,
|};

fiber中alternate切换 -- createWorkInProgress

先看函数

function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
  let workInProgress = current.alternate;
  // workInProgress === null 为 mount阶段
  if (workInProgress === null) {
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode,
    );
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;
    // currentFiber与newFiber连接
    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    // update阶段
    // 复用一些属性
    workInProgress.pendingProps = pendingProps;
    workInProgress.type = current.type;
    workInProgress.flags = NoFlags;
    workInProgress.subtreeFlags = NoFlags;
    workInProgress.deletions = null;
  }
  // flags置空 (除StaticMask)
  workInProgress.flags = current.flags & StaticMask;
  // 复用一些属性
  workInProgress.childLanes = current.childLanes;
  workInProgress.lanes = current.lanes;
  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;
  // 克隆 dependencies
  const currentDependencies = current.dependencies;
  workInProgress.dependencies =
    currentDependencies === null
      ? null
      : {
          lanes: currentDependencies.lanes,
          firstContext: currentDependencies.firstContext,
        };
  // 复用一些属性
  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;
  return workInProgress;
}
  • 时机:
    • diifreconcileChildFibers() 为 jsxChildren 创建 fiber 
    • 其中复用 fiber , 调用 createWorkInProgress() ,切换 workInProgress
    • 无法复用新创建的 fiber ,调用createFiberFromTypeAndProps() 创建 fiber 
  • workInProgress:
    • workInProgress 是新旧交替, alternate 是新旧的连接点  
      • 新旧fiber对象被复用,而不是新创建,应该是省内存空间
    • workInProgress 是本次更新对应的 fiber ,
      • alternate 则是上次更新对应的fiber(目前页面呈现的fiber)
  • alternate切换示意图
 update     a                                                      b
   0:     current(第一次为null)       <--alternate-->             workInProgress
 切换:                   workInProgress = current.alternate
                        workInProgress = createFiber()             ↓ (上次的workInProgress是本次的current)
                        (第一次时current为null,需要创建fiber)
   1:     workInProgress            <--alternate-->             current
 切换:       ↓           workInProgress = current.alternate
   2:     current                   <--alternate-->             workInProgress
 切换:                   workInProgress = current.alternate3:     workInProgress            <--alternate-->             current
  • 在最终渲染时,直接切换为 alternate 即可
const finishedWork: Fiber = root.current.alternate;
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
commitRoot(root);
root.current = finishedWork;  // commitRoot函数中执行

Fiber的头节点

// 伪代码 模拟表示头结点
root:FiberRootNode = {
  current:Fiber = {
    stateNode = root  // 这里是循环引用
    memoizedState = {element:null} // 这是项目JSX入口
    updateQueue // 初始化updateQueue
  }
}
  • root.current表示正在程序的fiber

参考

React技术揭秘