文件目录

packages/react-dom/src/ReactDOM.js

用法

ReactDOM.render(<App />, document.getElementById("root"));

源码

ReactDOM.render

const ReactDOM: Object = {
    /**
     * element => <App />
     * container => document.getElementById("root")
     * callback => 回调函数 很少用
    */
    render(
        element: React$Element<any>,
        container: DOMContainer,
        callback: ?Function,
    ) {
    // 下面第四个参数为false, 如果为true那么说明是服务端渲染
    return legacyRenderSubtreeIntoContainer(
        null,
        element,
        container,
        false,
        callback,
    );
};
  • 先为container创建_reactRootContainer属性,其类型是ReactRoot, 同时在这一步中删除掉container中的所有子集

legacyRenderSubtreeIntoContainer

/**
 * parentComponent => null
 * children => <App>
 * container => document.getElementById("root")
 * forceHydrate => false
 * callback => callback
*/
function legacyRenderSubtreeIntoContainer(
    parentComponent: ?React$Component<any, any>,
    children: ReactNodeList,
    container: DOMContainer,
    forceHydrate: boolean,
    callback: ?Function,
) {
    let root: Root = (container._reactRootContainer: any);
    // 当前 container 没有 _reactRootContainer, 进去if操作执行legacyCreateRootFromDOMContainer
    if (!root) {
        // 初始化操作,创建_reactRootContainer属性, 类型是ReactRoot
        root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
            container,
            forceHydrate,
        );
        // 反正我从没传过 callback,不关心实现
        if (typeof callback === 'function') {
            const originalCallback = callback;
            callback = function() {
                const instance = getPublicRootInstance(root._internalRoot);
                originalCallback.call(instance);
            };
        }
        // Initial mount should not be batched.
        // 第一次加载的话不需要打包更新
        unbatchedUpdates(() => {
            // 第一次不存在 parentComponent, 然后调用ReactRoot.prototype.render
            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 = getPublicRootInstance(root._internalRoot);
                originalCallback.call(instance);
            };
        }
        // Update
        if (parentComponent != null) {
            root.legacy_renderSubtreeIntoContainer(
                parentComponent,
                children,
                callback,
            );
        } else {
            root.render(children, callback);
        }
    }
    return getPublicRootInstance(root._internalRoot);
}

legacyCreateRootFromDOMContainer

  • 该方法是删除container下的子节点,并且生成ReactRoot
function legacyCreateRootFromDOMContainer(
    container: DOMContainer,
    forceHydrate: boolean,
): Root {
    // 这个变量跟服务端渲染有关,我们直接把他看成false
    const shouldHydrate =
        forceHydrate || shouldHydrateDueToLegacyHeuristic(container);

    if (!shouldHydrate) {
        let rootSibling;
        // 这里直接把container的子节点删除,直到没有子节点
        // <div id="root"></div>
        while ((rootSibling = container.lastChild)) {
            container.removeChild(rootSibling);
        }
    }

    // 默认情况下root不用异步
    const isConcurrent = false;
    return new ReactRoot(container, isConcurrent, shouldHydrate);
}

ReactRoot

  • 我们看ReactRoot这个结构函数 createContainer就是一行代码它的作用是创建FiberRoot, 而这个root 其实又是 FiberRootNode
  • 然后创建Fiber, createFiber(HostRoot, null, null, mode);, 然后又返回了FiberNode, 此时container拥有_reactRootContainer属性, 而 _reactRootContainer 拥有 _internalRoot属性, 而_internalRoot又拥有current属性,这个属性类型就是FiberNode
  • 我们可以在react 项目下 打印一下 document.getElementById("root")._reactRootContainer
/**
 * container => <div id="root"></div>
 * isConcurrent => false
 * hydrate => false
*/
function ReactRoot(
    container: DOMContainer,
    isConcurrent: boolean,
    hydrate: boolean,
) {
    // 这个 root 指的是 FiberRoot
    // 文件路径 packages/react-reconciler/src/ReactFiberReconciler.js
    // createContainer 很简单就一行代码 
    // return createFiberRoot(containerInfo, isConcurrent, hydrate);
    const root = createContainer(container, isConcurrent, hydrate);
    this._internalRoot = root;
}

ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  // 这里就是之前的那个createFiberRoot创建的FiberRoot
  const root = this._internalRoot;
  // ReactWork 的功能就是为了在组件渲染或更新后把所有传入
  // ReactDom.render 中的回调函数全部执行一遍
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;
  // 如果有 callback,就 push 进 work 中的数组
  if (callback !== null) {
    work.then(callback);
  }
  // work._onCommit 就是用于执行所有回调函数的
  updateContainer(children, root, null, work._onCommit);
  return work;
};

// 其实这里的名字已经表达了意思了,将subTree渲染进container
ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;

  if (callback !== null) {
    work.then(callback);
  }
  updateContainer(children, root, parentComponent, work._onCommit);
  return work;
};

createContainer

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 {
    // FiberRootNode 内部创建了很多属性
    const root: FiberRoot = (new FiberRootNode(containerInfo, hydrate): any);
    // document.getElementById("root").._reactRootContainer._internalRoot 是 FiberRoot
    // document.querySelector('#root')._reactRootContainer._internalRoot.current 是 Fiber
    const uninitializedFiber = createHostRootFiber(isConcurrent);
    root.current = uninitializedFiber;
    uninitializedFiber.stateNode = root;

    return root;
}

createHostRootFiber

export function createHostRootFiber(isConcurrent: boolean): Fiber {
    let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;

    if (enableProfilerTimer && isDevToolsPresent) {
        mode |= ProfileMode;
    }

    // HostRoot 指的是 classComponent, lazyComponent, functionCompoenent 等等
    return createFiber(HostRoot, null, null, mode);
}

createFiber

我们可以从 packages/react-reconciler/src/ReactFiber.js 查看Fiber 的属性

const createFiber = function(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
): Fiber {
  return new FiberNode(tag, pendingProps, key, mode);
};

FiberRoot 的结构

点击查看FiberRoot
type BaseFiberRootProperties = {|
  // Any additional information from the host associated with this root.
  // 就是 document.getElementById("root")
  containerInfo: any,
  // 仅用于持久更新。
  pendingChildren: any,
  // 当前应用对应的Fiber对象,是Root Fiber
  current: Fiber,

  // 以下优先级用于区分
  // 1) 没有提交(committed)的任务
  // 2) 没有提交的挂起任务
  // 3) 没有提交的可能被挂起的任务
  // 我们选择不追踪每个单独的阻塞登记,为了兼顾性能而是按交易粒度来衡量绩效。

  earliestSuspendedTime: ExpirationTime,
  latestSuspendedTime: ExpirationTime,
  // The earliest and latest priority levels that are not known to be suspended.
  earliestPendingTime: ExpirationTime,
  latestPendingTime: ExpirationTime,
  // 最新通过的一个被reslove并且可以重新尝试的优先级
  latestPingedTime: ExpirationTime,

  pingCache:
    | WeakMap<Thenable, Set<ExpirationTime>>
    | Map<Thenable, Set<ExpirationTime>>
    | null,

  // 如果抛出错误,并且队列中没有更多更新,则在处理该错误之前,我们尝试从根目录再次进行一次同步渲染。
  didError: boolean,

  // 正在等待提交的任务的`expirationTime`
  pendingCommitExpirationTime: ExpirationTime,

  finishedWork: Fiber | null,
  // 超时的操作
  timeoutHandle: TimeoutHandle | NoTimeout,
  // Top context object, used by renderSubtreeIntoContainer
  context: Object | null,
  pendingContext: Object | null,
  // Determines if we should attempt to hydrate on the initial mount
  +hydrate: boolean,
  // Remaining expiration time on this root.
  // TODO: Lift this into the 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
  firstBatch: Batch | null,
  // root之间关联的链表结构
  nextScheduledRoot: FiberRoot | null,

  // New Scheduler fields
  callbackNode: *,
  callbackExpirationTime: ExpirationTime,
  firstPendingTime: ExpirationTime,
  lastPendingTime: ExpirationTime,
  pingTime: ExpirationTime,
|};

type ProfilingOnlyFiberRootProperties = {|
  interactionThreadID: number,
  memoizedInteractions: Set<Interaction>,
  pendingInteractionMap: PendingInteractionMap,
|};

export type FiberRoot = {
  ...BaseFiberRootProperties,
  ...ProfilingOnlyFiberRootProperties,
};
FiberRoot 的结构是上面两个合起来

Fiber 结构

点击查看Fiber
export type Fiber = {|

  // 指的是functionComponent 还是 classComponent等等, lazyComponent
  tag: WorkTag,

  // Unique identifier of this child.
  key: null | string,

  // The value of element.type which is used to preserve the identity during
  // reconciliation of this child.
  elementType: any,

  // The resolved function/class/ associated with this fiber.
  type: any,

  //  本地相关联的Fiber
  stateNode: any,

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

  // 子节点,兄弟节点
  child: Fiber | null,
  sibling: Fiber | null,
  index: number,

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

  // 将要处理的props, 罪行的props
  pendingProps: any, // This type will be more specific once we overload the tag.
  // 上一次的props
  memoizedProps: any, // The props used to create the output.

  // A queue of state updates and callbacks.
  // 用来存放 update,也就是用来记录改变状态的
  updateQueue: UpdateQueue<any> | null,

  // 上一次的state
  memoizedState: any,

  // A linked-list of contexts that this fiber depends on
  contextDependencies: ContextDependencyList | null,
  mode: TypeOfMode,

  // Effect
  effectTag: SideEffectTag,

  // Singly linked list fast path to the next fiber with side-effects.
  nextEffect: Fiber | null,

  firstEffect: Fiber | null,
  lastEffect: Fiber | null,

  // Represents a time in the future by which this work should be completed.
  // Does not include work found in its subtree.
  expirationTime: ExpirationTime,

  // This is used to quickly determine if a subtree has no pending changes.
  childExpirationTime: ExpirationTime,

  alternate: Fiber | null,

  actualDuration?: number,

  actualStartTime?: number,

  selfBaseDuration?: number,

  treeBaseDuration?: number,

|};

Fiber结构图

Fiber结构图

函数运行流程图

运行流程图

从demo查看