render()
ReactDOM.render(element, container[, callback])
在提供的 container
里渲染一個 React 元素磁携,并返回對該組件的引用(或者針對無狀態(tài)組件返回 null
)。
如果 React 元素之前已經(jīng)在 container
里渲染過膝舅,這將會對其執(zhí)行更新操作,并僅會在必要時改變 DOM 以映射最新的 React 元素窑多。
如果提供了可選的回調(diào)函數(shù)铸史,該回調(diào)將在組件被渲染或更新之后被執(zhí)行。
注意:
ReactDOM.render()
會控制你傳入容器節(jié)點(diǎn)里的內(nèi)容怯伊。當(dāng)首次調(diào)用時琳轿,容器節(jié)點(diǎn)里的所有 DOM 元素都會被替換,后續(xù)的調(diào)用則會使用 React 的 DOM 差分算法(DOM diffing algorithm)進(jìn)行高效的更新耿芹。
ReactDOM.render()
不會修改容器節(jié)點(diǎn)(只會修改容器的子節(jié)點(diǎn))崭篡。可以在不覆蓋現(xiàn)有子節(jié)點(diǎn)的情況下吧秕,將組件插入已有的 DOM 節(jié)點(diǎn)中琉闪。
ReactDOM.render()
目前會返回對根組件ReactComponent
實(shí)例的引用。 但是砸彬,目前應(yīng)該避免使用返回的引用颠毙,因?yàn)樗菤v史遺留下來的內(nèi)容,而且在未來版本的 React 中砂碉,組件渲染在某些情況下可能會是異步的蛀蜜。 如果你真的需要獲得對根組件ReactComponent
實(shí)例的引用,那么推薦為根元素添加 callback ref增蹭。使用
ReactDOM.render()
對服務(wù)端渲染容器進(jìn)行 hydrate 操作的方式已經(jīng)被廢棄滴某,并且會在 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
方法中調(diào)用了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在執(zhí)行legacyCreateRootFromDOMContainer
的會清空該dom下的子元素,并返回一個ReactSyncRoot
類型的對象碍彭。并且第一次掛載的時候晤硕,shouldHydrate
是為false
的。 - 經(jīng)過
legacyCreateRootFromDOMContainer
之后硕旗,container
的數(shù)據(jù)是這樣的:
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的數(shù)據(jù)結(jié)構(gòu):
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;
}
接下去就是關(guān)鍵的updateContainer
函數(shù)了窗骑。這個函數(shù)牽涉的面比較多,我們下一片再說漆枚。