render()
ReactDOM.render(element, container[, callback])
在提供的 container
里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null
)。
如果 React 元素之前已经在 container
里渲染过,这将会对其执行更新操作,并仅会在必要时改变 DOM 以映射最新的 React 元素。
如果提供了可选的回调函数,该回调将在组件被渲染或更新之后被执行。
注意:
ReactDOM.render()
会控制你传入容器节点里的内容。当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 DOM 差分算法(DOM diffing algorithm)进行高效的更新。
ReactDOM.render()
不会修改容器节点(只会修改容器的子节点)。可以在不覆盖现有子节点的情况下,将组件插入已有的 DOM 节点中。
ReactDOM.render()
目前会返回对根组件ReactComponent
实例的引用。 但是,目前应该避免使用返回的引用,因为它是历史遗留下来的内容,而且在未来版本的 React 中,组件渲染在某些情况下可能会是异步的。 如果你真的需要获得对根组件ReactComponent
实例的引用,那么推荐为根元素添加 callback ref。使用
ReactDOM.render()
对服务端渲染容器进行 hydrate 操作的方式已经被废弃,并且会在 React 17 被移除。作为替代,请使用hydrate()
。
packages/react-dom/ReactDOM.js 源码解析
render(
element,
container,
callback,
) {
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
if (__DEV__) {
warningWithoutStack(
!container._reactHasBeenPassedToCreateRootDEV,
'You are calling ReactDOM.render() on a container that was previously ' +
'passed to ReactDOM.%s(). This is not supported. ' +
'Did you mean to call root.render(element)?',
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
);
}
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
在ReactDOM.render
方法中调用了legacyRenderSubtreeIntoContainer
方法:
function legacyRenderSubtreeIntoContainer(
parentComponent,
children,
container,
forceHydrate,
callback,
) {
if (__DEV__) {
topLevelUpdateWarnings(container);
warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
}
// TODO: Without `any` type, Flow says "Property cannot be accessed on any
// member of intersection type." Whyyyyyy.
let root = container._reactRootContainer;
let fiberRoot;
if (!root) {
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// Initial mount should not be batched.
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
- 如果没有
container._reactRootContainer
值,则说明是第一次挂载到该container
下,这时候,react在执行legacyCreateRootFromDOMContainer
的会清空该dom下的子元素,并返回一个ReactSyncRoot
类型的对象。并且第一次挂载的时候,shouldHydrate
是为false
的。 - 经过
legacyCreateRootFromDOMContainer
之后,container
的数据是这样的:
container._reactRootContainer = ReactRoot (from legacyCreateRootFromDOMContainer);
container._reactRootContainer._internalRoot = FiberRoot
container._reactRootContainer._internalRoot.current = FiberNode (HostRoot)
container._reactRootContainer._internalRoot.current.stateNode = container._reactRootContainer._internalRoot = FiberRoot
FiberRoot和Fiber的数据结构:
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.current = null;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.pingCache = null;
this.finishedExpirationTime = NoWork;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.firstBatch = null;
this.callbackNode = null;
this.callbackExpirationTime = NoWork;
this.firstPendingTime = NoWork;
this.lastPendingTime = NoWork;
this.pingTime = NoWork;
if (enableSchedulerTracing) {
this.interactionThreadID = unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
}
function FiberNode(
tag,
pendingProps,
key,
mode,
) {
// 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.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.expirationTime = NoWork;
this.childExpirationTime = NoWork;
this.alternate = null;
}
接下去就是关键的updateContainer
函数了。这个函数牵涉的面比较多,我们下一片再说。