此篇不关注服务端渲染的情况
本篇关注于 updateContainer之前处理逻辑
此篇关注 React 输入 updateContainer 之前的处理逻辑。
Legacy 模式下:
我们启动 React 开发的入口是;
React.render(<App />,document.getElementById('root'));
Render 方法的定义;
<T extends Element>(
element: DOMElement<DOMAttributes<T>, T>,
container: Container| null,
callback?: () => void
): T;
数据流向
经历 react render 函数以后,生成的数据关联性,如下
接下来我们逐层解析,render 函数究竟做了什么!
方法:
render
function render(element, container, callback) {
if (!isValidContainer(container)) {
{
throw Error( "Target container is not a DOM element." );
}
}
{
var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;
if (isModernRoot) {
error('You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOM.createRoot(). This is not supported. ' + 'Did you mean to call root.render(element)?');
}
}
return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
}
会执行 legacyRenderSubtreeIntoContainer 的方法。
- 第一个参数传递 null。
- 第二个参数是当前的元素。
- 第三个是当前承载根目录的容器。
- 第四个是服务端渲染
- 第五个是调用React.render 的时候传入的回调函数。
legacyRenderSubtreeIntoContainer
先给 react 容器添加一个元素 _reactRootContainer
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
{
topLevelUpdateWarnings(container);
warnOnInvalidCallback$1(callback === undefined ? null : callback, 'render');
}
//第一次的时候并没有_reactRootContainer ,此时的 root 为 undefined
var root = container._reactRootContainer;
var fiberRoot;
if (!root) {
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
var originalCallback = callback;
callback = function () {
var instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
} // Initial mount should not be batched.
unbatchedUpdates(function () {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
var _originalCallback = callback;
callback = function () {
var instance = getPublicRootInstance(fiberRoot);
_originalCallback.call(instance);
};
} // Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
legacyCreateRootFromDOMContainer
1、删除 react 容器中的子元素。
2、返回 createLegacyRoot
// 首次执行render的时候
// container: root 的 Dom 元素
//forceHydrate false
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content.
if (!shouldHydrate) {
var warned = false;
var rootSibling;
//删除容器中的 children
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.');
}
}
container.removeChild(rootSibling);
}
}
{
if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
warnedAboutHydrateAPI = true;
warn('render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v18. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.');
}
}
return createLegacyRoot(container, shouldHydrate ? {
hydrate: true
} : undefined);
}
createLegacyRoot
直接返回一个 ReactDOMBlockingRoot 的实例。
// container react 的容器
// options 是否为ssr
function createLegacyRoot(container, options) {
return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}
ReactDOMBlockingRoot
// container react 的容器(id为root 的根元素)
// tag 代表了当前 react 是什么模式。(concurrent、legacy、blocking)
var LegacyRoot = 0;
var BlockingRoot = 1;
var ConcurrentRoot = 2;
//这里的this指代的是 legacyRenderSubtreeIntoContainer 中的container._reactRootContainer
function ReactDOMBlockingRoot(container, tag, options) {
this._internalRoot = createRootImpl(container, tag, options);
}
createRootImpl
调用函数创建 FiberRoot 。
将 DOM 元素 与 FiberRoot 节点 绑定
function createRootImpl(container, tag, options) {
// 服务端渲染(ssr) 忽略
var hydrate = options != null && options.hydrate === true;
var hydrationCallbacks = options != null && options.hydrationOptions || null;
var mutableSources = options != null && options.hydrationOptions != null && options.hydrationOptions.mutableSources || null;
var root = createContainer(container, tag, hydrate);
markContainerAsRoot(root.current, container);
var containerNodeType = container.nodeType;
//忽略
{
var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement);
}
//服务端相关,忽略
if (mutableSources) {
for (var i = 0; i < mutableSources.length; i++) {
var mutableSource = mutableSources[i];
registerMutableSourceForHydration(root, mutableSource);
}
}
return root;
}
createContainer
function createContainer(containerInfo, tag, hydrate, hydrationCallbacks) {
return createFiberRoot(containerInfo, tag, hydrate);
}
createFiberRoot
1、创建 FiberRoot 节点(应用节点)。这里的 Tag 指代 React 当前的模式。
2、创建 rootFiber 节点 (Fiber节点)。这里的 Tag 指代 Fiber 节点属于哪一类。而 React 通过何种模式创建的则是挂在 mode 属性下面。
3、将 FiberRoot 节点 current 指针指向新创建的 RootFiber。
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
var root = new FiberRootNode(containerInfo, tag, hydrate);
var uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
// 更新队列,暂时不关心
initializeUpdateQueue(uninitializedFiber);
return root;
}
此时 root 变量(左图),此时的 current 为 null。
createFiberRoot 方法执行后, root 变量(右图)。 current 指向新创建的 RootFiber 并且在 RootFiber 中的stateNode 也指向了 FiberRoot 。
FiberRootNode
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.current = null;
this.pingCache = null;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.callbackNode = null;
this.callbackPriority = NoLanePriority;
this.eventTimes = createLaneMap(NoLanes);
this.expirationTimes = createLaneMap(NoTimestamp);
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes);
{
this.mutableSourceEagerHydrationData = null;
}
{
this.interactionThreadID = tracing.unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
{
switch (tag) {
case BlockingRoot:
this._debugRootType = 'createBlockingRoot()';
break;
case ConcurrentRoot:
this._debugRootType = 'createRoot()';
break;
case LegacyRoot:
this._debugRootType = 'createLegacyRoot()';
break;
}
}
}
createHostRootFiber
1、创建 RootFiber
2、判断当前的 mode,并传递个 RootFiber。这个属性将决定之后的工作流是是否分片
function createHostRootFiber(tag) {
var mode;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode | BlockingMode | StrictMode;
} else if (tag === BlockingRoot) {
mode = BlockingMode | StrictMode;
} else {
mode = NoMode;
}
if ( isDevToolsPresent) {
// Always collect profile timings when DevTools are present.
// This enables DevTools to start capturing timing at any point–
// Without some nodes in the tree having empty base times.
mode |= ProfileMode;
}
return createFiber(HostRoot, null, null, mode);
}
createFiber
var createFiber = function (tag, pendingProps, key, mode) {
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
return new FiberNode(tag, pendingProps, key, mode);
};
FiberNode
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.flags = NoFlags;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
{
// Note: The following is done to avoid a v8 performance cliff.
//
// Initializing the fields below to smis and later updating them with
// double values will cause Fibers to end up having separate shapes.
// This behavior/bug has something to do with Object.preventExtension().
// Fortunately this only impacts DEV builds.
// Unfortunately it makes React unusably slow for some applications.
// To work around this, initialize the fields below with doubles.
//
// Learn more about this here:
// https://github.com/facebook/react/issues/14365
// https://bugs.chromium.org/p/v8/issues/detail?id=8538
this.actualDuration = Number.NaN;
this.actualStartTime = Number.NaN;
this.selfBaseDuration = Number.NaN;
this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
// This won't trigger the performance cliff mentioned above,
// and it simplifies other profiler code (including DevTools).
this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}
{
// This isn't directly used but is handy for debugging internals:
this._debugID = debugCounter++;
this._debugSource = null;
this._debugOwner = null;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
}
}
markContainerAsRoot
var randomKey = Math.random().toString(36).slice(2);
var internalContainerInstanceKey = '__reactContainer$' + randomKey;
function markContainerAsRoot(hostRoot, node) {
node[internalContainerInstanceKey] = hostRoot;
}
updateContainer
进入到生成 Fiber 树的递与归阶段,本篇暂不讲解。
Tag
RootFiber Tag
在 react-reconciler 包的 ReactWorkTags.js 中找到对应定义。
var FunctionComponent = 0;
var ClassComponent = 1;
var IndeterminateComponent = 2; // Before we know whether it is function or class
var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
var HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
var HostComponent = 5;
var HostText = 6;
var Fragment = 7;
var Mode = 8;
var ContextConsumer = 9;
var ContextProvider = 10;
var ForwardRef = 11;
var Profiler = 12;
var SuspenseComponent = 13;
var MemoComponent = 14;
var SimpleMemoComponent = 15;
var LazyComponent = 16;
var IncompleteClassComponent = 17;
var DehydratedFragment = 18;
var SuspenseListComponent = 19;
var FundamentalComponent = 20;
var ScopeComponent = 21;
var Block = 22;
var OffscreenComponent = 23;
var LegacyHiddenComponent = 24;
FiberRoot Tag
在 react-reconciler 包的 ReactRootTags.js 中找到对应定义。
// ReactDOMRoot.js
import {
BlockingRoot,
ConcurrentRoot,
LegacyRoot,
} from 'react-reconciler/src/ReactRootTags';
export type RootTag = 0 | 1 | 2;
export const LegacyRoot = 0;
export const BlockingRoot = 1;
export const ConcurrentRoot = 2;
Node Type
在 react-dom 包的 HTMLNodeType.js 中找到对应定义。
export const ELEMENT_NODE = 1;
export const TEXT_NODE = 3;
export const COMMENT_NODE = 8;
export const DOCUMENT_NODE = 9;
export const DOCUMENT_FRAGMENT_NODE = 11;