文件目录
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,
};
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,
|};