[Reactソースコードシリーズ]2-ノードの初期化
前のリンク:Reactソースコード学習シリーズ1-構成と操作
この記事は、主にポイントを必死に中断し、新しい名詞(関数)を覚えることについてです。理由(なぜこのように実行されるのか)ではなく、何(ここで何が実行されるのか)の質問を見てください。
-
走る()
このコードはbabelによって実行され、関数は次のとおりです。
function run(transformFn, script) { var scriptEl = document.createElement("script"); scriptEl.text = transformCode(transformFn, script); headEl.appendChild(scriptEl); }
なかでも、バベルの中
transformFn
からやってくるのですが、今はReactを学んでいるので、バベルにはあまり時間をかけていません。script
内容は以下の通りです。const container = document.getElementById("container"); // Create a root. const root = ReactDOM.createRoot(container); // Initial render root.render(<h1>Hello World</h1>);
headEl
これはbabel内の変数であり、この一般的な実行ロジックは、生成されしてレンダリングを完了することだと思います。scriptEl
-
createRoot()
追加する場合は、最初にオブジェクトを作成する必要があります。
興味深いのは、実際に
createRoot()
はです。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); }
それから再び本当
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); }
いくつかの調査
$
のは公式には無意味ですが、慣例では$
、関数の後に続く関数は1を返します。Observable
[1]
-
createContainer
この関数の実行結果はテストポイントです。
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 ); }
はい、伝説のファイバーはここから始まりました。
-
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
ここでもキーワード:並行。
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); }
ご覧のとおり、
createHostRootFiber
通常のFiberNodeとの最大の違いはおそらく初期化です。mode
-
ファイバーノード
ファイバーノードの初期化については何も言うことはありませんが、ソースコードには興味深いコメントがあります。
注:以下は、v8パフォーマンスの低下を回避するために行われます。
以下のフィールドをsmisに初期化し、後でそれらを
double値で更新すると、ファイバーは別々の形状になります。
この動作/バグはObject.preventExtension()と関係があります。
幸い、これはDEVビルドにのみ影響します。
残念ながら、一部のアプリケーションではReactが使用できないほど遅くなります。
これを回避するには、以下のフィールドをdoubleで初期化します。詳細については、こちらをご覧ください。
https://github.com/facebook/react/issues/14365
https://bugs.chromium.org/p/v8/issues/detail?id=8538
この問題とこの発生したバグについては後で確認する予定です。
-
markContainerAsRoot
このコードは単純です:
function markContainerAsRoot(hostRoot, node) { node[internalContainerInstanceKey] = hostRoot; }
internalContainerInstanceKey
名前は、現在のインスタンスを管理するための一意のキーである必要があります。後で、このキーがどのように生成されるか、および各更新との関係を確認することもできます。 -
listenToAllSupportedEvents
ここでは、イベントハンドラーを個別に分解します。Reactが内部でイベント処理の処理も行っていることがわかります。
-
新しいReactDOMRoot(root)
これは実際には内部ノードをバインドしています。
function ReactDOMRoot(internalRoot) { this._internalRoot = internalRoot; }
その後、このノードは最も早い追加セグメントに戻されます。
この時点で、空白のノード(Node)が生成されました。次のステップは、新しい要素を
createElement
作成し、render
それを介してレンダリングすることです。
フローチャット
プロセスツリーを更新します。