十日谈 :React的更新(上)

欢迎阅读我的React源码学习笔记

首先了解

创建更新的方式

  • ReactDOM.render 或者 hydrate
  • setState
  • forceUpdate

所以这篇文章主要围绕这三个API进行源码的阅读

更新的步骤

  • 创建ReactRoot
  • 创建FiberRootRootFiber
  • 创建更新

react-dom-render

这一部分主要熟悉流程
我们先找到ReactDom的定义

ReactDOM.render是React的最基本方法用于将模板转为HTML语言,并插入指定的DOM节点。ReactDOM.render(template,targetDOM),该方法接收两个参数:第一个是创建的模板,多个dom元素外层需使用一个标签进行包裹,如<div>;第二个参数是插入该模板的目标位置。

const ReactDOM: Object = {
	...
}

其中包含一个render方法,我们先来看一看

ReactDom.render函数有两个功能,1、判断传入的容器是否是dom元素,2、调用legacyRenderSubtreeIntoContainer函数

  render(
    element: React$Element<any>,
    container: DOMContainer,
    callback: ?Function,
  ) {
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  },

我们可以看到这个方法直接return了一个叫legacyRenderSubtreeIntoContainer,并把自己的参数又作为这个函数的参数进行了传递,看一下这个函数:

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean, // 是否复用现有的dom节点,通常用于服务端渲染(hydrate方法调用时必传true)
  callback: ?Function,
) {
  // TODO: Ensure all entry points contain this check
  invariant(
    isValidContainer(container),
    'Target container is not a DOM element.',
  );

  if (__DEV__) {
    topLevelUpdateWarnings(container);
  }

  // TODO: Without `any` type, Flow says "Property cannot be accessed on any
  // member of intersection type." Whyyyyyy.
  let root: Root = (container._reactRootContainer: any);
  //container就是我们传入的dom节点
  if (!root) {
  //第一渲染的时候root为空,所以会进入这个判断
    // Initial mount
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(// 第一次 创建容器[1]
      container,
      forceHydrate,
    );
    //[5]
    if (typeof callback === 'function') {
    //简单的封装callback
      const originalCallback = callback;
      callback = function() {
        const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    DOMRenderer.unbatchedUpdates(() => { // 第一次渲染时,该回调直接执行
      if (parentComponent != null) {
        root.legacy_renderSubtreeIntoContainer(
          parentComponent,
          children,
          callback,
        );
      } else {
        root.render(children, callback);
      }
    });
  } else {
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    if (parentComponent != null) {
      root.legacy_renderSubtreeIntoContainer(
        parentComponent,
        children,
        callback,
      );
    } else {
      root.render(children, callback);
      //[6]
    }
  }
  return DOMRenderer.getPublicRootInstance(root._internalRoot);
}

注意:代码中添加部分注释帮助理解
[1]legacyCreateRootFromDOMContainer()方法

function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  forceHydrate: boolean,
): Root {
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  // First clear any existing content.
  if (!shouldHydrate) {
    let warned = false;
    let rootSibling;
    while ((rootSibling = container.lastChild)) {
    //[2]while循环的功能
      if (__DEV__) {
        if (
          !warned &&
          rootSibling.nodeType === ELEMENT_NODE &&
          (rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME)
        ) {
          warned = true;
          warningWithoutStack(
            false,
            'render(): Target node has markup rendered by React, but there ' +
              'are unrelated nodes as well. This is most commonly caused by ' +
              'white-space inserted around server-rendered markup.',
          );
        }
      }
      container.removeChild(rootSibling);
    }
  }
  if (__DEV__) {
    if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
      warnedAboutHydrateAPI = true;
      lowPriorityWarning(
        false,
        'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
          'will stop working in React v17. Replace the ReactDOM.render() call ' +
          'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
      );
    }
  }
  // Legacy roots are not async by default.
  const isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
  //[3]
}

我们传入的container是一个dom元素,所以没有_reactRootContainer属性,会调用legacyCreateRootFromDOMContainer方法,给该方法传入了container forceHydrate参数。forceHydrate主要来区别是服务端渲染(hydrate)还是客户端渲染(render)这个参数在render方法中默认的是false,这其实也是hydrate和render唯一的区别,就是服务端在渲染的时候,第一次的渲染结果应该和客户端是一致的,所以可以进行复用,于是hydrate中的这个参数保持true,而在render中保持false。
[2]shouldHydrate 标记了这个标签是否开启服务器渲染,在没有开启服务器渲染的时候,进入while循环,这个循环的主要功能就是删除container的所以子元素。
[3] 最后return了一个 new ReactRoot(container, isConcurrent, shouldHydrate)
其中由于Legacy roots are not async by default.所以参数isConcurrent直接被指定为false
再看一下new ReactRoot的方式

function ReactRoot(
  container: Container,
  isConcurrent: boolean,
  hydrate: boolean,
) {
  const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}

实际上是通过DOMRenderer.createContainer创建了一个root标签(实际上创建的是一个FiberRooter)。

[5] 实际上,root = container._reactRootContainer是将new ReactRoot赋值给了root,到这里是完成了一个创建root的过程

[6] 我们看一下ReactRoot.prototype.render的方法是怎么做的

ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;
  if (__DEV__) {
    warnOnInvalidCallback(callback, 'render');
  }
  if (callback !== null) {
    work.then(callback);
  }
  DOMRenderer.updateContainer(children, root, null, work._onCommit);
  //这一步至关重要[7]
  return work;
};

[7]updateContainer()方法接受四个参数
(element,container,parentComponent,callback)
第一个参数是实际的组件也就是我们脚手架上的<App>
而这里第二个参数是root,不再是曾经的container
第三个参数在ReactDOM.render时是null
第四个参数经过封装成为了work._onCommit

export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): ExpirationTime {
  const current = container.current;
  const currentTime = requestCurrentTime();
  const expirationTime = computeExpirationForFiber(currentTime, current);
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}

后续我们可以看到一大堆渲染的调度相关的方法的调用,大概是updateContainerAtExpirationTimescheduleRootUpdatecreateUpdateenqueueUpdatescheduleWork

插入一个小技巧,关于BookMarks的使用

VSCODE可以安装BookMarks扩展插件,在阅读单个文件中的长篇幅代码段时,可以通过Ctrl+Alt+k设置书签,然后使用Ctrl+Alt+L/Ctrl+Alt+J可以实现左右书签跳转,非常方便。

稍微提一下关于判断服务端渲染的方式:

function shouldHydrateDueToLegacyHeuristic(container) {
  const rootElement = getReactRootElementInContainer(container);
  return !!(
    rootElement &&
    rootElement.nodeType === ELEMENT_NODE &&
    rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
  );
}

ROOT_ATTRIBUTE_NAME作为一个标记,表示如果标签上带着data-reactroot属性,那么表示是服务端渲染

export const ROOT_ATTRIBUTE_NAME = 'data-reactroot';

总结一下

ReactDom.render函数主要是处理传入的root节点,创建一个ReactRoot,同时创建一个FiberRoot,创建FiberRoot的过程中也会创建一个FiberRoot对象,根据创建的FiberRoot去更新。

react-fiber-root

什么是FiberRoot

  • 整个应用的起点
  • 包含应用挂载的目标节点
  • 记录整个应用更新过程的各种信息

调用的地点我们可以看到[3]之下的createContainer方法创建了是一个FiberRooter,我们来看看代码

export function createContainer(
  containerInfo: Container,
  isConcurrent: boolean,
  hydrate: boolean,
): OpaqueRoot {
  return createFiberRoot(containerInfo, isConcurrent, hydrate);
}

我们可以看到传入参数包含三个,并且返回了createFiberRoot方法。

export function createFiberRoot(
  containerInfo: any,
  isConcurrent: boolean,
  hydrate: boolean,
): FiberRoot {
  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  const uninitializedFiber = createHostRootFiber(isConcurrent);

  let root;
  if (enableSchedulerTracing) {
    root = ({
      current: uninitializedFiber, // 当前应用对应的Fiber对象
      containerInfo: containerInfo,
      pendingChildren: null,

      earliestPendingTime: NoWork,
      latestPendingTime: NoWork,
      earliestSuspendedTime: NoWork,
      latestSuspendedTime: NoWork,
      latestPingedTime: NoWork,

      didError: false,

      pendingCommitExpirationTime: NoWork,
      finishedWork: null,
      timeoutHandle: noTimeout,
      context: null,
      pendingContext: null,
      hydrate,
      nextExpirationTimeToWorkOn: NoWork,
      expirationTime: NoWork,
      firstBatch: null,
      nextScheduledRoot: null,

      interactionThreadID: unstable_getThreadID(),
      memoizedInteractions: new Set(),
      pendingInteractionMap: new Map(),
    }: FiberRoot);
  } else {
    root = ({
      current: uninitializedFiber,
      containerInfo: containerInfo,
      pendingChildren: null,

      earliestPendingTime: NoWork,
      latestPendingTime: NoWork,
      earliestSuspendedTime: NoWork,
      latestSuspendedTime: NoWork,
      latestPingedTime: NoWork,

      didError: false,

      pendingCommitExpirationTime: NoWork,
      finishedWork: null,
      timeoutHandle: noTimeout,
      context: null,
      pendingContext: null,
      hydrate,
      nextExpirationTimeToWorkOn: NoWork,
      expirationTime: NoWork,
      firstBatch: null,
      nextScheduledRoot: null,
    }: BaseFiberRootProperties);
  }

  uninitializedFiber.stateNode = root;

  // The reason for the way the Flow types are structured in this file,
  // Is to avoid needing :any casts everywhere interaction tracing fields are used.
  // Unfortunately that requires an :any cast for non-interaction tracing capable builds.
  // $FlowFixMe Remove this :any cast and replace it with something better.
  return ((root: any): FiberRoot);
}

我们可以看到这个方法返回了FiberRoot

const uninitializedFiber = createHostRootFiber(isConcurrent);

创建了一个Fiber对象
这里我们可以学习一下react中的数据结构,从而了解一下Fiber对象,详见文章附录

react-fiber

根据视频内容参考简书博客React源码解析之RootFiber

作用

  • (1)每一个ReactElement对应一个Fiber对象
  • (2)记录节点的各种状态,比如ClassComponent中的stateprops的状态就是记录在Fiber对象上的。
    只有当Fiber对象更新后,才会更新到ClassComponent上的this.statethis.props
    this上的stateprops是根据Fiber对象的stateprops更新的。
    这实际上也方便了ReactHooks,因为hooks是为FunctionalComponent服务的。虽然FunctionalComponent没有this,但Fiber上有,是可以拿到stateprops
  • (3)串联整个应用形成树结构 每个ReactElement通过props.children与其他ReactElement连结起来

注意:在串联时,children只指向第一个子元素节点,其他的子元素节点作为第一个子元素节点的兄弟节点用sibling指向。
图1

这里使用的数据结构是附录Fiber中的:

type Fiber =  {|
...
  // 跟当前Fiber相关本地状态(比如浏览器环境就是DOM节点)
  stateNode: any,

  // 指向他在Fiber节点树中的`parent`,用来在处理完这个节点之后向上返回
  return: Fiber | null,

  // 单链表树结构
  // 指向自己的第一个子节点
  child: Fiber | null,
  // 指向自己的兄弟结构
  // 兄弟节点的return指向同一个父节点
  sibling: Fiber | null,
...
|};
  • FiberRootcurrent属性指向一个Fiber对象,叫 RootFiber
  • RootFiberstateNode 指向 FiberRoot
  • FiberRoot接收的App对象,也就是根对象,它就是RootFiberchild,里面的div之类的props.children数组的第一个会作为它的child,也是FIber对象,第二个就会成为childsibling

这样进行节点的查找就非常方便,通过child一路向下找,找到叶子节点,判断有没有兄弟节点,有就遍历兄弟节点,继续这个循环,最后没有字节点也没有兄弟节点了就会返回通过return,最终返回到最上面
串联过程:
① 任一 叶子 节点A,如果有兄弟节点,则去单向向后遍历兄弟节点,最后return到父节点
② 父节点的child节点不是刚刚的子节点A的话,则从child节点遍历到A前的节点,并再次return到父节点
③ 该父节点执行 ①、②

根据图1举例:
比如从左下角的input节点开始,它没有兄弟节点,则return到父组件Input(因为父节点有且只有一个,所以必定return到父节点)

Input有兄弟节点List,List又有child节点,则从child节点往后单向遍历兄弟节点,最后return到List

List又return到div,div的child节点已被遍历,则return到App节点,App,App又return到所有Fiber对象的根对象RootFiber对象

这样,就将整个应用遍历完了

react-update-and-updataQueue

Updata

  • (1)用来记录组件的状态变化
  • (2)存放在UpdateQueue中
  • (3)多个Update可以同时存在

什么叫做多个Update可以同时存在呢,其实是指在调用多个setState的情况下,不会分多次进行更新,而是将多个setState放进队列进行合并更新。

入口是scheduleRootUpdatecreateUpdate,我们来看看源码

export const UpdateState = 0;
export const ReplaceState = 1;
export const ForceUpdate = 2;
export const CaptureUpdate = 3;

export function createUpdate(
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
): Update<*> {
  return {
    //更新的过期时间
    expirationTime,
    suspenseConfig,

    // export const UpdateState = 0;
    // export const ReplaceState = 1;
    // export const ForceUpdate = 2;
    // export const CaptureUpdate = 3;

    //重点提下CaptureUpdate,在React16后有一个ErrorBoundaries功能
    //即在渲染过程中报错了,可以选择新的渲染状态(提示有错误的状态),来更新页面
    tag: UpdateState, //0更新 1替换 2强制更新 3捕获性的更新

    //更新内容,比如setState接收的第一个参数
    payload: null,

    //对应的回调,比如setState({}, callback )
    callback: null,

    //指向下一个更新
    next: null,

    //指向下一个side effect
    nextEffect: null,
  };
}

入参是我们之前提到通过计算获得的expirationTime,直接返回一个对象,这个对象中的属性都可以在附录中找到对应数据结构加以了解。

updateQueue

updateQueue是一个带有优先级的链表,主要是为了保证updata的次序。

//创建更新队列
export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
  const queue: UpdateQueue<State> = {
    //应用更新后的state
    baseState,
    //队列中的第一个update
    firstUpdate: null,
    //队列中的最后一个update
    lastUpdate: null,
    //队列中第一个捕获类型的update
    firstCapturedUpdate: null,
    //队列中最后一个捕获类型的update
    lastCapturedUpdate: null,
    //第一个side effect
    firstEffect: null,
    //最后一个side effect
    lastEffect: null,
    firstCapturedEffect: null,
    lastCapturedEffect: null,
  };
  return queue;
}

这个比较容易理解,实质上的作用就是

  • (1)baseState在组件setState后,渲染并更新state,在下次更新时,拿的就是这次更新过的state
  • (2)firstUpdatelastUpdate之间的update通过上个updatenext串联

enqueueUpdate

enqueueUpdate是一个单向链表,用来存放updatenext来串联update

//每次setState都会update,每次update,都会入updateQueue
//current即fiber
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  // Update queues are created lazily.
  //alternate即workInProgress
  //fiber即current

  //current到alternate即workInProgress有一个映射关系
  //所以要保证current和workInProgress的updateQueue是一致的
  const alternate = fiber.alternate;
  //current的队列
  let queue1;
  //alternate的队列
  let queue2;
  //如果alternate为空
  if (alternate === null) {
    // There's only one fiber.
    queue1 = fiber.updateQueue;
    queue2 = null;
    //如果queue1仍为空,则初始化更新队列
    if (queue1 === null) {
      queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
    }
  } else {
    // There are two owners.
    //如果alternate不为空,则取各自的更新队列
    queue1 = fiber.updateQueue;
    queue2 = alternate.updateQueue;
    if (queue1 === null) {
      if (queue2 === null) {
        // Neither fiber has an update queue. Create new ones.
        //初始化
        queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
        queue2 = alternate.updateQueue = createUpdateQueue(
          alternate.memoizedState,
        );
      } else {
        // Only one fiber has an update queue. Clone to create a new one.
        //如果queue2存在但queue1不存在的话,则根据queue2复制queue1
        queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
      }
    } else {
      if (queue2 === null) {
        // Only one fiber has an update queue. Clone to create a new one.
        queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
      } else {
        // Both owners have an update queue.
      }
    }
  }
  if (queue2 === null || queue1 === queue2) {
    // There's only a single queue.
    //将update放入queue1中
    appendUpdateToQueue(queue1, update);
  } else {
    // There are two queues. We need to append the update to both queues,
    // while accounting for the persistent structure of the list — we don't
    // want the same update to be added multiple times.
    //react不想多次将同一个的update放入队列中
    //如果两个都是空队列,则添加update
    if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
      // One of the queues is not empty. We must add the update to both queues.
      appendUpdateToQueue(queue1, update);
      appendUpdateToQueue(queue2, update);
    }
    //如果两个都不是空队列,由于两个结构共享,所以只在queue1加入update
    //在queue2中,将lastUpdate指向update
    else {
      // Both queues are non-empty. The last update is the same in both lists,
      // because of structural sharing. So, only append to one of the lists.
      appendUpdateToQueue(queue1, update);
      // But we still need to update the `lastUpdate` pointer of queue2.
      queue2.lastUpdate = update;
    }
  }
}

更新队列是成对出现的(源码enqueueUpdate方法queue1&queue2):

  • 一个当前队列,queue1取的是fiber.updateQueue
  • 一个work-in-progress队列,queue2取的是alternate.updateQueue,在提交前可以异步修改和处理。如果一个正在进行的渲染在完成之前被销毁,我们则可以通过克隆当前队列来创建一个新的正在进行的工作。
    如果两者均为null,则调用createUpdateQueue()获取初始队列
    如果两者之一为null,则调用cloneUpdateQueue()从对方中获取队列
    如果两者均不为null,则将update作为lastUpdate

这个方法实际上是创建和更新updataQueue的过程

总结

FiberRoot
在ReactDOM.render的过程中创建了一个ReactRoot对象,这个对象最主要的就是承担了去创建一个FiberRoot对象,很重要,在后期整个应用的调度过程中都会跟它有关。FiberRoot是整个应用的起点,包含应用挂载的目标节点,记录整个应用更新过程的各种信息,因为应用更新过程中,会涉及到各种个样的东西,比如各种类型的expirationTime,异步调度任务的callback,都会记录在FiberRoot上面。它是在createContainer里调用createFiberRoot创建的。

Fiber
每一个ReactElement都会对应一个Fiber对象,记录节点的各种状态,比如class组件的state,还有props,然后Fiber更新后才会更新到this.state里面,更新了这个节点之后,才会把属性放到this上面,所以才会为hooks的实现提供了方便,因为state之类的都记在了Fiber上面,这样没有this也可以 。此外Fiber串联了整个应用形成树结构,把每个节点串联起来。

Update & updateQueue
Update是用来记录组件状态改变的对象,存在于Fiber的updateQueue里,单向链表的结构,可以算出最终的state的结果。多个Update可以同时存在,比如调用三次setState,就会产生三个Update,不会一次setState就更新一下,而是会等三个setState执行完,三个Update创建完,放到updateQueue里面,再进行更新

附录:react中的数据结构

FiberRoot

type BaseFiberRootProperties = {|
  // 页面上挂载的dom节点, 就是render方法接收的第二个参数
  containerInfo: any,
  // 只有在持久更新中会用到,也就是不支持增量更新的平台,react-dom不会用到
  pendingChildren: any,
  // 当前树的根节点,就是FiberRoot
  current: Fiber,

  // 以下的优先级是用来区分
  // 1、没有提交(committed)的任务
  // 2、没有提交的挂起的任务
  // 3、没有提交的可能被挂起的任务
  // 我们选择不追踪每个单独的阻塞登记,为了兼顾性能
  
  // The earliest and latest priority levels that are suspended from committing.
  // 提交时候被挂起的最老和最新的任务
  earliestSuspendedTime: ExpirationTime,
  latestSuspendedTime: ExpirationTime,
  
  // The earliest and latest priority levels that are not known to be suspended.
  // 在提交时候可能会被挂起的最老和最新的任务(所有任务进来都是这个状态)
  earliestPendingTime: ExpirationTime,
  latestPendingTime: ExpirationTime,
  // The latest priority level that was pinged by a resolved promise and can
  // be retried.
  // 最新的通过一个promise被resolve并且可以重新尝试的优先级
  latestPingedTime: ExpirationTime,

  // 如果有错误被抛出并且没有更多的更新存在,我们尝试在处理错误前同步重新从头渲染
  // 在`renderRoot`出现无法处理的错误时会被设置为`true`
  didError: boolean,

	// 正在等待提交的任务的`expirationTime`
  pendingCommitExpirationTime: ExpirationTime,
  // 已经完成的任务的FiberRoot对象,如果你只有一个Root,那他永远只可能是这个Root对应的Fiber,或者是null
  // 在commit阶段只会处理这个值对应的任务
  finishedWork: Fiber | null,
  // 在任务被挂起的时候通过setTimeouth函数的返回值
  // 用来清理下一次如果有新的任务挂起时还没触发的timeout
  timeoutHandle: TimeoutHandle | NoTimeout,
  // 顶层context对象,只有主动调用`renderSubtreeIntoContainer`时才会有用
  context: Object | null,
  pendingContext: Object | null,
  // 用来确定第一次渲染的时候是否需要融合
  +hydrate: boolean,
  // 当前root上剩余的过期时间
  // TODO: 提到renderer里面区处理
  nextExpirationTimeToWorkOn: ExpirationTime,
  // 当前更新对应的过期时间
  expirationTime: ExpirationTime,
  // List of top-level batches. This list indicates whether a commit should be
  // deferred. Also contains completion callbacks.
  // TODO: Lift this into the renderer
  // 顶层批次(批处理任务?)这个变量指明一个commit是否应该被推迟
  // 同时包括完成之后的回调
  // 貌似用在测试的时候?
  firstBatch: Batch | null,
  // Linked-list of roots
  // next
  nextScheduledRoot: FiberRoot | null,
|};

Fiber

// Fiber对应一个组件需要被处理或者已经处理了,一个组件可以有一个或者多个Fiber
type Fiber =  {|
  // Fiber的tag,用来标记不同的组件类型
  tag: WorkTag,

  // 就是组件的那个key
  key: null | string,

  // 我们调用`createElement`的第一个参数 div/p/func/class
  elementType: any,

  // 异步组件resolved之后返回的内容,一般是`function`或者`class`
  type: any,

  // 跟当前Fiber相关本地状态(比如浏览器环境就是DOM节点)
  stateNode: any,

  // 指向他在Fiber节点树中的`parent`,用来在处理完这个节点之后向上返回
  return: Fiber | null,

  // 单链表树结构
  // 指向自己的第一个子节点
  child: Fiber | null,
  // 指向自己的兄弟结构
  // 兄弟节点的return指向同一个父节点
  sibling: Fiber | null,
  index: number,

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

  // 新的变动带来的新的props,就是nextProps
  pendingProps: any, 
  // 上一次渲染完成之后的propP,就是当前props
  memoizedProps: any, 

  // 该Fiber对应的组件产生的Update会存放在这个队列里面
  updateQueue: UpdateQueue<any> | null,

  // 上一次渲染的时候的state
  memoizedState: any,

  // 一个列表,存放这个Fiber依赖的context
  firstContextDependency: ContextDependency<mixed> | null,

  // 用来描述当前Fiber和他子树的`Bitfield`
  // 共存的模式表示这个子树是否默认是异步渲染的
  // Fiber被创建的时候他会继承父Fiber
  // 其他的标识也可以在创建的时候被设置
  // 但是在创建之后不应该再被修改,特别是他的子Fiber创建之前
  mode: TypeOfMode,

  // Effect
  // 用来记录Side Effect
  effectTag: SideEffectTag,

  // 单链表用来快速查找下一个side effect
  nextEffect: Fiber | null,

  // 子树中第一个side effect
  firstEffect: Fiber | null,
  // 子树中最后一个side effect
  lastEffect: Fiber | null,

  // 代表任务在未来的哪个时间点应该被完成
  // 不包括他的子树产生的任务
  expirationTime: ExpirationTime,

  // 快速确定子树中是否有不在等待的变化
  childExpirationTime: ExpirationTime,

  // 在Fiber树更新的过程中,每个Fiber都会有一个跟其对应的Fiber
  // 我们称他为`current <==> workInProgress`
  // 在渲染完成之后他们会交换位置
  alternate: Fiber | null,

  // 下面是调试相关的,收集每个Fiber和子树渲染时间的
   
  actualDuration?: number,

  // If the Fiber is currently active in the "render" phase,
  // This marks the time at which the work began.
  // This field is only set when the enableProfilerTimer flag is enabled.
  actualStartTime?: number,

  // Duration of the most recent render time for this Fiber.
  // This value is not updated when we bailout for memoization purposes.
  // This field is only set when the enableProfilerTimer flag is enabled.
  selfBaseDuration?: number,

  // Sum of base times for all descedents of this Fiber.
  // This value bubbles up during the "complete" phase.
  // This field is only set when the enableProfilerTimer flag is enabled.
  treeBaseDuration?: number,

  // Conceptual aliases
  // workInProgress : Fiber ->  alternate The alternate used for reuse happens
  // to be the same as work in progress.
  // __DEV__ only
  _debugID?: number,
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
|};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值