[React source code series] 2 - Node initialization
Previous link: React source code learning series 1 - configuration and operation
This article is mainly about frantically interrupting points and remembering new nouns (functions). It does not involve why (why it runs like this), but simply look at the questions of what (what is executed here).
-
run()
This piece of code is executed by babel, and the function is as follows:
function run(transformFn, script) { var scriptEl = document.createElement("script"); scriptEl.text = transformCode(transformFn, script); headEl.appendChild(scriptEl); }
Among them, it
transformFn
comes from inside babel. Since I am learning React now, I don't spend much time on babel.script
The content is as follows:const container = document.getElementById("container"); // Create a root. const root = ReactDOM.createRoot(container); // Initial render root.render(<h1>Hello World</h1>);
headEl
It is a variable inside babel. I guess this general execution logic is to append thescriptEl
generated result to this variable controlled by babel to complete the rendering. -
createRoot()
If you want to append, you must first create an object.
The interesting thing is that there
createRoot()
are calls here:function createRoot$1(container, options) { { if (!Internals.usingClientEntryPoint) { error( 'You are importing createRoot from "react-dom" which is not supported. ' + 'You should instead import it from "react-dom/client".' ); } } return createRoot(container, options); }
Then again true
createRoot()
:function createRoot(container, options) { if (!isValidContainer(container)) { throw new Error( "createRoot(...): Target container is not a DOM element." ); } warnIfReactDOMContainerInDEV(container); var isStrictMode = false; var concurrentUpdatesByDefaultOverride = false; var identifierPrefix = ""; var onRecoverableError = defaultOnRecoverableError; var transitionCallbacks = null; if (options !== null && options !== undefined) { { // 省略一堆 warning 和 error } if (options.unstable_strictMode === true) { isStrictMode = true; } if (options.identifierPrefix !== undefined) { identifierPrefix = options.identifierPrefix; } if (options.onRecoverableError !== undefined) { onRecoverableError = options.onRecoverableError; } if (options.transitionCallbacks !== undefined) { transitionCallbacks = options.transitionCallbacks; } } var root = createContainer( container, ConcurrentRoot, false, null, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError ); markContainerAsRoot(root.current, container); var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container; listenToAllSupportedEvents(rootContainerElement); return new ReactDOMRoot(root); }
After some investigation,
$
the is officially meaningless, but the convention is that$
the function followed by will return one .Observable
[1]
-
createContainer
The execution result of this function is the test point:
function createContainer( containerInfo, tag, // TODO: We can remove hydration-specific stuff from createContainer once // we delete legacy mode. The new root API uses createHydrationContainer. hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks ) { return createFiberRoot( containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError ); }
Yes, the legendary Fiber started here.
-
createFiberRoot
function createFiberRoot( containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, // TODO: We have several of these arguments that are conceptually part of the // host config, but because they are passed in at runtime, we have to thread // them through the root constructor. Perhaps we should put them all into a // single type, like a DynamicHostConfig that is defined by the renderer. identifierPrefix, onRecoverableError, transitionCallbacks ) { var root = new FiberRootNode( containerInfo, tag, hydrate, identifierPrefix, onRecoverableError ); // stateNode is any. var uninitializedFiber = createHostRootFiber(tag, isStrictMode); root.current = uninitializedFiber; uninitializedFiber.stateNode = root; { var initialCache = createCache(); retainCache(initialCache); // The pooledCache is a fresh cache instance that is used temporarily // for newly mounted boundaries during a render. In general, the // pooledCache is always cleared from the root at the end of a render: // it is either released when render commits, or moved to an Offscreen // component if rendering suspends. Because the lifetime of the pooled // cache is distinct from the main memoizedState.cache, it must be // retained separately. root.pooledCache = initialCache; retainCache(initialCache); var initialState = { element: null, cache: initialCache, transitions: null, }; uninitializedFiber.memoizedState = initialState; } initializeUpdateQueue(uninitializedFiber); return root; }
-
createHostRootFiber
Here again the keyword: concurrent.
function createHostRootFiber( tag, isStrictMode, concurrentUpdatesByDefaultOverride ) { var mode; if (tag === ConcurrentRoot) { mode = ConcurrentMode; if (isStrictMode === true) { mode |= StrictLegacyMode; { mode |= StrictEffectsMode; } } } 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); }
As you can see,
createHostRootFiber
the biggest differencemode
from . -
Fiber Node
There is nothing to say about the initialization of Fiber Node, but there is an interesting comment in the source code:
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
I plan to take a look at this issue and this raised bug later.
-
markContainerAsRoot
This code is simple:
function markContainerAsRoot(hostRoot, node) { node[internalContainerInstanceKey] = hostRoot; }
internalContainerInstanceKey
The name should be a unique key to manage the current instance. Later, you can also find out how this key is generated and what is the relationship with each update. -
listenToAllSupportedEvents
Event Handler will be disassembled separately here. It can be seen that React has also done some processing on event handling internally.
-
new ReactDOMRoot(root)
This is actually binding an internal node:
function ReactDOMRoot(internalRoot) { this._internalRoot = internalRoot; }
After that, this node is returned to the earliest append segment.
At this point, a blank node (Node) has been generated, and the next step is to
createElement
create a new element andrender
render it through .
flowchat
Update the process tree: