点击这里进入react原理专栏
这篇文章来看一下react应用的入口:ReactDOM.render的流程。ReactDOM.render主要做了三件事:创建整个应用的根fiber节点:fiberRoot,合成事件的处理以及挂载应用。
render方法会调用legacyRenderSubtreeIntoContainer方法
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
var root = container._reactRootContainer;
var fiberRoot;
if (!root) {
// 初次挂载,创建根节点
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
fiberRoot = root._internalRoot;
// 应用的挂载
unbatchedUpdates(function () {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
// 。。。
}
return getPublicRootInstance(fiberRoot);
}
legacyCreateRootFromDOMContainer方法源码如下
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
if (!shouldHydrate) {
var warned = false;
var rootSibling;
while (rootSibling = container.lastChild) {
{
if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) {
warned = true;
error('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.');
}
}
// 清空root节点内的dom元素
container.removeChild(rootSibling);
}
}
// 。。。
return createLegacyRoot(container, shouldHydrate ? {
hydrate: true
} : undefined);
}
这个方法会清空root元素内的dom节点,之后经过一系列方法调用,来到了createRootImpl
function createRootImpl(container, tag, options) {
// ...
var root = createContainer(container, tag, hydrate);
{
var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement);
}
// ...
return root;
}
这个方法主要干了两件事:创建fiberRoot,以及合成事件的处理。分别对应createContainer方法和listenToAllSupportedEvents方法。合成事件的原理会有文章进行介绍。
至此,legacyCreateRootFromDOMContainer方法执行完毕,接下来开始挂载整个应用,进入updateContainer方法(unbatchedUpdates先略过)。updateContainer方法会调用scheduleUpdateOnFiber方法,开始进行更新(这个方法就是更新react应用的入口)。
下面看scheduleUpdateOnFiber方法的这段逻辑:
if (
(executionContext & LegacyUnbatchedContext) !== NoContext &&
(executionContext & (RenderContext | CommitContext)) === NoContext) {
// ReactDOM.render进入这里
schedulePendingInteractions(root, lane);
performSyncWorkOnRoot(root);
} else {
// 后续更新进入这里
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, lane);
if (executionContext === NoContext) {
resetRenderTimer();
flushSyncCallbackQueue();
}
}
现在再看unbatchedUpdates方法
function unbatchedUpdates(fn, a) {
var prevExecutionContext = executionContext;
executionContext &= ~BatchedContext;
executionContext |= LegacyUnbatchedContext;
try {
return fn(a);
} finally {
executionContext = prevExecutionContext;
// ...
}
}
unbatchedUpdates方法执行了executionContext |= LegacyUnbatchedContext,因此之后到unbatchedUpdates方法中,executionContext & LegacyUnbatchedContext) !== NoContext为true,而RenderContext和CommitContext时在render阶段和commit阶段被设置的,因此executionContext & (RenderContext | CommitContext)) === NoContext为true。之后进入了performSyncWorkOnRoot(后续更新的过程也会调用performSyncWorkOnRoot,只是调用方式不同)。
进入performSyncWorkOnRoot中有两个重要的方法调用:renderRootSync和commitRoot,分别对应render阶段和commit阶段的入口。这些内容会有专门的文章进行讲解。
本文讲解了整个react的入口ReactDOM.render的部分流程,这里提到了合成事件,render和commit阶段,这些内容都会有相应的文章进行讲解,敬请期待!!