React 18におけるFiberツリーの初期構築メカニズム

初期構築プロセスの核心

Fiberツリーの構築は2つの主要シナリオで発生します:

  • 初回構築:アプリケーション起動時、DOMが未構築の状態。新規ツリーを直接生成します
  • 差分更新:既存のUIが存在する場合、新旧Fiberノードを比較して更新を適用します

本解説ではLegacyモード下での初回構築に焦点を当て、コアメカニズムを解説します。

サンプルコードの再構成

class メインコンポーネント extends React.Component {
  componentDidMount() {
    console.log('メインコンポーネントマウント');
  }
  render() {
    return (
      <section className="main">
        <header>ヘッダー</header>
        <コンテンツ領域 />
      </section>
    );
  }
}

class コンテンツ領域 extends React.Component {
  componentDidMount() {
    console.log('コンテンツ領域マウント');
  }
  render() {
    return (
      <React.Fragment>
        <div>アイテム1</div>
        <div>アイテム2</div>
      </React.Fragment>
    );
  }
}
export default メインコンポーネント;

レンダリング開始フロー

react-reconcilerパッケージ内の更新処理開始関数が起点となります:

function 更新処理開始(
  要素: ReactNode,
  コンテナ: ルートオブジェクト,
  親コンポーネント: ?ReactComponent,
  コールバック: ?Function
): 優先度レベル {
  const 現在のFiber = コンテナ.現在のノード;
  const イベントタイムスタンプ = タイムスタンプ取得();

  // 優先度レベルの計算(車線モデル)
  const 優先度 = 優先度レベル計算(現在のFiber);

  // 更新オブジェクトの作成とキュー登録
  const 更新レコード = 作成更新レコード(イベントタイムスタンプ, 優先度);
  更新レコード.ペイロード = { 要素 };
  更新キュー追加(現在のFiber, 更新レコード);

  // レンダリングスケジューリング
  スケジューリング実行(現在のFiber, 優先度, イベントタイムスタンプ);
  return 優先度;
}

この処理でHostRootFiber.updateQueueにReact要素が格納され、メモリ構造が初期化されます。

同期レンダリングの実行

Legacyモードでは同期ルート処理実行関数がコアロジックを担当します:

function 同期ルート処理実行(ルート: ファイバールート) {
  // レンダリング優先度の決定
  const レンダリング優先度 = 次回レンダリング優先度取得(ルート);

  // 深さ優先探索によるツリー構築
  レンダリングループ同期(ルート, レンダリング優先度);

  // 完成ツリーのコミット準備
  ルート.完了作業 = ルート.現在のノード.代替;
  ルート.完了優先度 = レンダリング優先度;
  
  // DOM反映フェーズへ移行
  コミットルート(ルート);
}

作業ループの動作原理

深さ優先探索を実現する作業ループ同期の実装:

function 作業ループ同期() {
  while (作業進行中ノード !== null) {
    作業単位実行(作業進行中ノード);
  }
}

function 作業単位実行(単位作業: Fiber): void {
  const 現在ノード = 単位作業.代替;
  let 次ノード;

  // 探索フェーズ:子ノードの生成
  次ノード = 開始作業(現在ノード, 単位作業, サブツリー優先度);

  単位作業.メモ化プロップス = 単位作業.保留プロップス;

  if (次ノード === null) {
    // 帰りがけ順処理の開始
    完了作業単位(単位作業);
  } else {
    作業進行中ノード = 次ノード;
  }
}

このループで作業進行中ノードポインタがツリーをトラバースします。

探索フェーズの詳細

開始作業関数はノードタイプごとに処理を分岐します:

function 開始作業(
  現在: Fiber | null,
  作業中: Fiber,
  レンダリング優先度: 優先度レベル
): Fiber | null {
  // 優先度のリセット
  作業中.レーン = 最高優先度;

  switch (作業中.タグ) {
    case クラスコンポーネント:
      return クラスコンポーネント更新(
        現在,
        作業中,
        作業中.タイプ,
        作業中.保留プロップス,
        レンダリング優先度
      );
    case ホストルート:
      return ホストルート更新(現在, 作業中, レンダリング優先度);
    case ホスト要素:
      return ホスト要素更新(現在, 作業中, レンダリング優先度);
    // その他のケース
  }
}

主な処理ステップ:

  1. 入力状態から出力状態を計算(memoizedStateの設定)
  2. 下位のReact要素を取得(render()実行など)
  3. reconcileChildrenで子Fiberノードを生成

帰りがけ順処理の実装

完了作業単位関数がDOM生成を担当します:

function 完了作業単位(単位作業: Fiber): void {
  let 完了ノード = 単位作業;
  
  do {
    const 親Fiber = 完了ノード.親;
    
    if ((完了ノード.フラグ & 不完全) === 通常フラグ) {
      // DOMインスタンスの生成
      完了作業(完了ノード);
      
      // 副作用キューの統合
      if (親Fiber !== null) {
        if (親Fiber.最初の副作用 === null) {
          親Fiber.最初の副作用 = 完了ノード.最初の副作用;
        }
        if (完了ノード.最後の副作用 !== null) {
          if (親Fiber.最後の副作用 !== null) {
            親Fiber.最後の副作用.次 = 完了ノード.最初の副作用;
          }
          親Fiber.最後の副作用 = 完了ノード.最後の副作用;
        }
        
        // ノード自体の副作用をキューに追加
        if (完了ノード.フラグ > 実行済み作業) {
          if (親Fiber.最後の副作用 !== null) {
            親Fiber.最後の副作用.次 = 完了ノード;
          } else {
            親Fiber.最初の副作用 = 完了ノード;
          }
          親Fiber.最後の副作用 = 完了ノード;
        }
      }
    }
    
    // 兄弟ノードの処理へ
    if (完了ノード.兄弟 !== null) {
      作業進行中ノード = 完了ノード.兄弟;
      return;
    }
    
    // 親ノードへ戻る
    完了ノード = 親Fiber;
    作業進行中ノード = 完了ノード;
    
  } while (完了ノード !== null);
}

特にホスト要素タイプでは:

  1. DOMインスタンスの生成(createInstance
  2. 子要素のDOMを親にアペンド
  3. 属性設定とイベントバインディング

実行フローの可視化

サンプルコードの処理シーケンス:

  1. HostRootFiberでメインコンポーネントを生成(Placementフラグ設定)
  2. メインコンポーネントでsection要素を生成
  3. sectionでheaderを生成 → 文字列子要素のためHostText不要
  4. headerの完了時にDOMインスタンスを生成
  5. 次にコンテンツ領域を処理 → 2つのdivを生成
  6. 各divで文字列子要素を処理 → DOMインスタンス生成
  7. 帰りがけ順で親ノードに副作用キューを統合

この過程で副作用キューが構築され、最終的にルートノードに統合されます。キューの順序は深さ優先の帰りがけ順となり、DOM操作の実行順序を決定します。

メモリ構造の変化

構築完了時点で重要な変更:

  • FiberRoot.finishedWorkが新規Fiberツリーを指す
  • HostRootFiberに副作用キューが構築される
  • 各HostComponentノードのstateNodeがDOMインスタンスを参照

この状態でcommitRootが呼び出され、実際のDOM操作が実行されます。

タグ: react-fiber reconciliation-algorithm legacy-mode-rendering

5月18日 20:24 投稿