反応ソースコードで ReactDOM.render が実行された後はどうなりますか?

ReactDOM.render

通常、次の図に示すように、提供されたコンテナーで React 要素をレンダリングし、コンポーネントへの参照 (ステートレス コンポーネントの場合は null) を返すために使用されます。この記事では、以降の記事で主に ReactDOM.render の実行プロセスを分析して、作成と更新の詳細を分析します (記事のソース コード部分は、__DEV__読みやすいようにコードの一部を削除しています)。

ReactDOM.render(<App />,document.getElementById('root')
); 

与える

寝転ぶ:react-dom/src/client/ReactDOMLegacy.js

export function render( element: React$Element<any>,container: Container,callback: ?Function, ) {// 验证container是否为有效的DOM节点invariant(isValidContainer(container),'Target container is not a DOM element.',);return legacyRenderSubtreeIntoContainer(null,element,container,false,callback,);
} 

legacyRenderSubtreeIntoContainer 関数が返されます。ここでは、5 つのパラメーターがあることに注意してください。

parentComponent: 初めて作成されるため、親コンポーネントは null です。

children: 入ってくる ReactElement

container: React の DOM コンテナをレンダリングします

forceHydrate:調整が必要かどうかを判断する. サーバー側レンダリングの場合、レンダリングされた DOM 構造は類似しているため、比較して再利用できます。サーバー側レンダリングの場合に ReactDOM.hydrate() を使用することは、forceHydrate が true としてマークされることを除いて、render() と同じです。

callback: レンダリング後のコールバック関数

legacyRenderSubtreeIntoContainer

所属:react-dom/src/client/ReactDOMLegacy.js役割:

  • それが最初のレンダリングであるかどうかを判断します。そうであれば、ルートを作成し、root._internalRoot を fiberRoot に割り当て、コールバック コールバックをカプセル化してから、unbatchedUpdates を呼び出して子ノードをすぐに更新します。
  • 最初のレンダリングでない場合は、通常の updateContainer プロセスに入ります。
  • 最後に、getPublicRootInstance(fiberRoot) はパブリック ルート インスタンス オブジェクトを返します。
function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>,children: ReactNodeList,container: Container,forceHydrate: boolean,callback: ?Function, ) {// TODO: Without `any` type, Flow says "Property cannot be accessed on any// member of intersection type." Whyyyyyy.let root: RootType = (container._reactRootContainer: any);let fiberRoot;if (!root) {// Initial mount 初次渲染创建FiberRootroot = 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);};}// UpdateupdateContainer(children, fiberRoot, parentComponent, callback);}return getPublicRootInstance(fiberRoot);
} 

legacyCreateRootFromDOMContainer

場所:react-dom/src/client/ReactDOMLegacy.js初期レンダリングはルート作成のリンクに入ります:root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate)機能: 主にサーバー側のレンダリングかどうかを判断します。そうであれば、調整のために既存の dom ノードを再利用してパフォーマンスを向上させます。そうでない場合は、子要素をクリアします。最後にコンテナと shouldHydrate を渡して createLegacyRoot 関数を返します。

function legacyCreateRootFromDOMContainer( container: Container,forceHydrate: boolean, ): RootType {const shouldHydrate =forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // 判断是否是服务端渲染// First clear any existing content.if (!shouldHydrate) {let warned = false;let rootSibling;while ((rootSibling = container.lastChild)) {container.removeChild(rootSibling);}}return createLegacyRoot(container,shouldHydrate? {hydrate: true,}: undefined,);
} 

createLegacyRoot

場所:react-dom/src/client/ReactDOMRoot.js機能: ReactDOMBlockingRoot のインスタンスが返され、ここで渡される LegacyRoot は定数 = 0 で、現在使用されている同期レンダリング モードを表し、後続の同時割り込み可能レンダリング モード用です。

export function createLegacyRoot(container: Container,options?: RootOptions, // hydrate
): RootType {return new ReactDOMBlockingRoot(container, LegacyRoot, options);
} 

ReactDOMBlockingRoot

react-dom/src/client/ReactDOMRoot.js関数: 関数createRootImpl戻り値 (FiberRoot) をインスタンスの _internalRoot にマウントします。

function ReactDOMBlockingRoot( container: Container,tag: RootTag,options: void | RootOptions, ) {this._internalRoot = createRootImpl(container, tag, options);
} 

関連参考動画解説:エンターラーニング

createRootImpl

場所:react-dom/src/client/ReactDOMRoot.js機能: createContainer を実行して FiberRootNode を取得し、ルートに割り当ててから、markContainerAsRoot を介して RootFiber をコンテナーにマウントします。

function createRootImpl( container: Container,tag: RootTag,options: void | RootOptions, ) {// Tag is either LegacyRoot or Concurrent Rootconst hydrate = options != null && options.hydrate === true;const hydrationCallbacks =(options != null && options.hydrationOptions) || null;// 拿到FiberRootNodeconst root = createContainer(container, tag, hydrate, hydrationCallbacks);// 将FiberRootNode挂载到containermarkContainerAsRoot(root.current, container);if (hydrate && tag !== LegacyRoot) {const doc =container.nodeType === DOCUMENT_NODE? container: container.ownerDocument;eagerlyTrapReplayableEvents(container, doc);}return root;
} 

createContainer

場所:react-reconciler/src/ReactFiberReconciler.old.js機能: createFiberRoot に戻る

export function createContainer(containerInfo: Container,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): OpaqueRoot {return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
} 

createFiberRoot

場所:react-reconciler/src/react-reconciler/src/ReactFiberReconciler.old.js機能: 新しい FiberRoot オブジェクトを作成してルートに割り当て、Fiber (通常は RootFiber と呼ばれます) を初期化して、root.current = uninitializedFiber および uninitializedFiber.stateNode = root を介して 2 つを接続します。実行しinitializeUpdateQueue(uninitializedFiber)て更新キューを作成し、fiber.updateQueue をマウントし、最後にルートを返します

export function createFiberRoot(containerInfo: any,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): FiberRoot {// 新建fiberRoot对象const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);if (enableSuspenseCallback) {root.hydrationCallbacks = hydrationCallbacks;}// Cyclic construction. This cheats the type system right now because// stateNode is any.//初始化RootFiberconst uninitializedFiber = createHostRootFiber(tag);root.current = uninitializedFiber;// RootFiber的stateNode指向FiberRootuninitializedFiber.stateNode = root;initializeUpdateQueue(uninitializedFiber);return root;
} 

FiberRoot RootFiber と updateQueue

ReactDOM.render は主に、FiberRooat、RootFiber、Updatequeue の 3 つのオブジェクトを作成します。この 3 つのオブジェクトを分析してみましょう。

ファイバールート

FiberRoot は、次の場所にある FiberRootNode (containerInfo、タグ、水和物) のインスタンスですreact-reconciler/src/ReactFiberRoot/FiberRootNode

  • アプリケーション全体の出発点
  • アプリケーション マウント用のターゲット ノードが含まれています
  • アプリケーションの更新プロセス全体でさまざまな情報を記録します
function FiberRootNode(containerInfo, tag, hydrate) {// 标记不同的组件类型this.tag = tag;// 当前应用对应的Fiber对象,是Root Fiber// current:Fiber对象 对应的是 root 节点,即整个应用根对象this.current = null;// root节点,render方法接收的第二个参数this.containerInfo = containerInfo; // 只有在持久更新中会用到,也就是不支持增量更新的平台,react-dom不会用到this.pendingChildren = null;this.pingCache = null;//任务有三种,优先级有高低://(1)没有提交的任务//(2)没有提交的被挂起的任务//(3)没有提交的可能被挂起的任务 //当前更新对应的过期时间this.finishedExpirationTime = NoWork;//已经完成任务的FiberRoot对象,如果你只有一个Root,那么该对象就是这个Root对应的Fiber或null//在commit(提交)阶段只会处理该值对应的任务this.finishedWork = null;// 在任务被挂起的时候通过setTimeout设置的返回内容,用来下一次如果有新的任务挂起时清理还没触发的timeout(例如suspense返回的promise)this.timeoutHandle = noTimeout;// 顶层context对象,只有主动调用renderSubTreeIntoContainer时才会被调用this.context = null;this.pendingContext = null;// 第一次渲染是否需要调和this.hydrate = hydrate;// Node returned by Scheduler.scheduleCallbackthis.callbackNode = null;this.callbackPriority = NoPriority;//存在root中,最旧的挂起时间//不确定是否挂起的状态(所有任务一开始均是该状态)this.firstPendingTime = NoWork;this.firstSuspendedTime = NoWork;this.lastSuspendedTime = NoWork;this.nextKnownPendingLevel = NoWork;//存在root中,最新的挂起时间//不确定是否挂起的状态(所有任务一开始均是该状态)this.lastPingedTime = NoWork;this.lastExpiredTime = NoWork;this.mutableSourcePendingUpdateTime = NoWork;if (enableSchedulerTracing) {this.interactionThreadID = unstable_getThreadID();this.memoizedInteractions = new Set();this.pendingInteractionMap = new Map();}if (enableSuspenseCallback) {this.hydrationCallbacks = null;}
} 

ルートファイバー

RootFiber はconst uninitializedFiber = createHostRootFiber(tag) 通过 createFiber 返回 FiberNode的实例機能するように初期化されます。

  • 各 ReactElement は Fibre オブジェクトに対応します
  • ノードのさまざまな状態を記録します (Fiber の完了後に記録状態と props がこれにマウントされるため、フックに便利です。例: pendingProps pendingState memoizedProps memoizedState)
  • アプリケーション全体を連結してツリー構造を形成する
// 位于 react-reconciler/src/ReactFiber.js
export function createHostRootFiber(tag: RootTag): Fiber {let mode;if (tag === ConcurrentRoot) {mode = ConcurrentMode | BlockingMode | StrictMode;} else if (tag === BlockingRoot) {mode = BlockingMode | StrictMode;} else {mode = NoMode;}return createFiber(HostRoot, null, null, mode);
}

const createFiber = function(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode,
): Fiber {return new FiberNode(tag, pendingProps, key, mode);
};

// FiberNode结构
function FiberNode(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode,
) {// Instance// 标记不同的组件类型this.tag = tag;// ReactElement里面的keythis.key = key;// ReactElement.type,也就是我们调用`createElement`的第一个参数this.elementType = null;// 异步组件lazy component resolved之后返回的内容,一般是`function`或者`class`组件this.type = null;// 对应节点的实例,比如类组件就是class的实例,如果是dom组件就是dom实例,如果是function component就没有实例这里为空this.stateNode = null;// Fiber Fiber是个链表通过child和Sibling连接,遍历的时候先遍历child如果没有子元素了则访问return回到上级查询是否有sibling// 指向他在Fiber节点树中的‘parent’,用来在处理完这个节点之后向上返回this.return = null;// 指向第一个子节点this.child = null;// 指向自己的兄弟节点,兄弟节点的return指向同一个副节点this.sibling = null;this.index = 0;this.ref = null;// 新的变动带来的新的propsthis.pendingProps = pendingProps;// 上次渲染完成后的propsthis.memoizedProps = null;// 该Fiber对应的组件产生的update会存放在这个队列(比如setState和forceUpdate创建的更新)this.updateQueue = null;// 上一次的statethis.memoizedState = null;this.dependencies = null;// this.mode = mode;// Effects// 用来记录副作用this.effectTag = NoEffect;// 单链表用来快速查找下一个side effectthis.nextEffect = null;// 子树中第一个side effectthis.firstEffect = null;// 子树中最后一个side effectthis.lastEffect = null;// 代表任务在未来的哪个时候应该被完成 就是过期时间// 不包括他的子树产生的任务this.expirationTime = NoWork;// 快速确定子树中是否有不再等待的变化this.childExpirationTime = NoWork;// Fiber树更新过程中,每个FIber都会有一个跟其对应的Fiber// 我们称他为`current <==> workInProgress`// 渲染完成后他们会交换位置this.alternate = null;// 调试相关的去掉了

} 

updateQueue

initializeUpdateQueue(uninitializedFiber);場所:react-reconciler/src/ReactUpdateQueue.js機能: 一方向のリンクされたリスト、更新を格納するために使用され、更新を直列に接続するために使用されます. Update と UpdateQueue には多くのことが関係しており、別の章で説明する予定です.

export function initializeUpdateQueue<State>(fiber: Fiber): void {const queue: UpdateQueue<State> = {// 每次操作完更新阿之后的statebaseState: fiber.memoizedState,// 队列中的第一个`Update`firstBaseUpdate: null,// 队列中的最后一个`Update`lastBaseUpdate: null,shared: {pending: null,},effects: null,};fiber.updateQueue = queue;
} 

フローチャート

最後に、描かれた一般的なフローチャート

やっと

75のJSの高頻度インタビュー質問を整理し、回答と分析を提供しました.これは、JSに関するインタビューアーの質問に基本的に対処できることを保証できます.



困っている友達は、下のカードをクリックして無料で受け取り、共有できます

おすすめ

転載: blog.csdn.net/web22050702/article/details/128640039