Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Beyond 1,000 Rows: Building a TypeScript Data Grid That Actually Scales Across React, Vue, and Angular

2026年11月03日に「Dev.to」が公開したITニュース「Beyond 1,000 Rows: Building a TypeScript Data Grid That Actually Scales Across React, Vue, and Angular」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

大量データを扱うデータグリッドは1000行を超えると動作が遅くなる。この記事では、TypeScriptでフレームワーク非依存のコアを構築し、表示される行だけをレンダリングする「仮想化」技術を用いる。これにより、React, Vue, Angularといった主要フレームワークで10万行以上のデータをスムーズに扱う方法を解説する。パフォーマンス改善の鍵はアーキテクチャにある。

ITニュース解説

ウェブアプリケーションにおいて、数多くの情報を一覧表示する「データグリッド」は頻繁に利用されるUI要素である。しかし、このグリッドに表示するデータが1000行を超えると、多くのウェブアプリケーションで動作が遅くなる、スクロールがカクつくといったパフォーマンスの問題が発生し始める。このような問題は、最終的に数万、数十万といった大規模なデータを扱う際に、アプリケーションの利用体験を著しく損ねる要因となる。

データグリッドが1000行を超えた途端にパフォーマンスが低下する主要な原因はいくつかある。まず一つ目は「DOMサイズ」の肥大化である。ウェブページを構成する最小単位の要素は「DOMノード」と呼ばれ、例えばテーブルの各行(<tr>タグ)や各セル(<td>タグ)がこれに該当する。1000行のデータグリッドは、それだけで1000個以上の行ノードと、その中に含まれる多数のセルノードを生成する。これはブラウザにとってすでに重い処理だが、10000行ともなると、全体のDOMノードの数は10万個を優に超える。ブラウザはこれらの膨大なノードの生成、スタイルの適用、そして画面上の配置計算に多大な時間を費やすため、実際のコンテンツの表示が追いつかなくなる。

二つ目の原因は、React、Vue、Angularといったモダンなウェブフレームワークの「リアクティビティシステム」の特性に起因するボトルネックである。これらのフレームワークは、データの変更を検知し、効率的に画面を更新する仕組み(Reactの仮想DOM、Vueのリアクティビティ、Angularの変更検出)を持っている。しかし、大量のデータを扱うデータグリッドにおいて、少しでもデータが更新されると、フレームワークはデータセット全体を走査して変更箇所を特定しようとする。この全データ走査の処理が、データ量が増えるほどに時間を要し、アプリケーションの応答性を低下させる。

三つ目の原因は「スクロールパフォーマンス」の悪化である。ユーザーが画面をスクロールするたびに、ブラウザは毎秒60回以上という高頻度でスクロールイベントを発行する。このイベントが発生するたびに、データグリッドの全要素を再描画しようとすると、フレームワークとブラウザの両方に過大な負荷がかかり、結果として滑らかなスクロールが不可能となる。

これらのパフォーマンス問題を根本的に解決するためには、フレームワークの機能に頼り切るのではなく、そのボトルネックを回避するような、より洗練されたアーキテクチャを採用する必要がある。その解決策として提案されるのが、「フレームワーク非依存のコア」と「薄いアダプター層」からなる設計パターンである。

このアーキテクチャの中心となるのは、特定のフレームワークに一切依存しない、純粋なTypeScriptで書かれた「コア」部分である。このコアは、データグリッドのデータの管理、行の選択、データのソート、そして「仮想化」と呼ばれる表示計算ロジックといった、純粋なビジネスロジックと状態管理の責任を負う。重要なのは、このコアがブラウザのDOM(画面表示要素)を一切操作しない点である。コアはデータを処理し、その状態が変化した際にイベントを発行するのみである。

一方、「アダプター」と呼ばれる層は、React、Vue、Angularといった各フレームワーク向けに個別に実装される。このアダプターの役割は非常にシンプルで、コアから発行されるイベントを購読し、そのイベントに応じて、現在画面に表示されるべきDOM要素をレンダリングすることだけを行う。アダプターは、コアが計算した「現在画面に見えている行」だけを責任を持って描画するため、フレームワークが実際に操作するDOMノードの数を大幅に削減し、パフォーマンスのボトルネックを解消できる。

この設計パターンの要となる技術が「仮想化」である。仮想化とは、データグリッドが持つ膨大な全データの中から、現在ユーザーの画面に表示されている部分(これを「ビューポート」と呼ぶ)と、その上下に少しの余裕(「バッファ」)を持たせた行のみを実際にDOMとしてレンダリングする手法である。例えば、10万行のデータグリッドであっても、行の高さが40ピクセル、ビューポートの高さが600ピクセルであれば、実際にDOMとして描画されるのはわずか16行程度となる。ユーザーがスクロールするたびに、コアは現在のスクロール位置、ビューポートの高さ、各行の高さといった情報に基づいて、どの行を表示すべきかを数学的に計算し、その結果をアダプターに伝える。アダプターはその計算結果に基づき、必要最小限のDOM要素だけをレンダリングすることで、常に少ないDOMノードでデータグリッドを構成するのである。スクロールの際に画面がちらつくのを防ぐため、可視領域の上下に数行分のバッファを持たせる「オーバースキャン」というテクニックも利用される。

コアとアダプター間の状態管理は、イベント駆動型で行われる。コアはデータや状態が変化するたびに、その変更内容を示すイベントを発行する。アダプターはそのイベントを購読し、受け取った情報に基づいて自身のフレームワークの状態を更新し、最終的に画面の再描画を促す。これにより、フレームワーク側がコアの状態を常に監視して変更をチェックする「ポーリング」の必要がなくなり、無駄な処理が削減される。例えば、ユーザーがスクロールした際、アダプターはコアから新しい可視行のリストを取得し、そのリストに基づいてDOMを更新する。この際、フレームワークの不要な再レンダリングを防ぐための最適化も重要である。ReactではuseMemoフックやReact.memoでコンポーネントの再描画を抑制し、Vueではcomputedプロパティやv-memo、AngularではChangeDetectionStrategy.OnPushやtrackByディレクティブを用いて、変更があった部分のみを効率的に更新するようにする。

具体的な実装例を考えると、まず純粋なTypeScriptでGridCoreというクラスを定義する。このクラスは、すべての行データ、カラム(列)の定義、ソートの状態、選択されている行の情報、そしてスクロール位置やビューポートの高さといったデータグリッドの状態を管理する。GridCoreは、データをソートするsortByメソッドや、行を選択するselectRowメソッド、そして何より重要な、現在表示すべき行のリストを計算するgetVisibleRowsメソッドを持つ。これらのメソッドが呼び出され、内部の状態が変化するたびに、GridCoreはイベントをリスナー(アダプター)に通知する。

次に、React用の「Reactアダプター」を実装する。このアダプターは、ReactのuseStateフックを使ってスクロール位置を管理し、useMemoフックでGridCoreのgetVisibleRowsメソッドを呼び出し、表示すべき行のリストを効率的に取得する。また、useEffectフックを使ってGridCoreから発行されるイベントを購読し、データが更新された場合にReactの状態を更新して再レンダリングをトリガーする。HTMLのdiv要素にスクロールイベントリスナーを設定し、スクロール位置が変化するたびにuseStateを更新する。最終的に、map関数を使って可視行のリストだけをDOMとしてレンダリングし、各行のコンポーネントはReact.memoでラップして、不要な再レンダリングを防ぐ。VueやAngularのアダプターも、それぞれのフレームワークのリアクティビティシステムやライフサイクルフック、変更検出の仕組みを活用して同様に実装される。

このように構築されたデータグリッドは、例えば10万行のユーザーデータを扱うような場面でも、メモリ上にはすべてのデータが存在するが、実際にブラウザに描画されるDOM要素はごく少数の行のみとなる。ユーザーが特定のカラムでデータをソートすると、その操作はGridCore内部で処理され、コアが発行するイベントを通じてアダプターが可視行だけを再描画するため、大規模なデータであっても非常にスムーズな操作感を実現できる。

さらなるパフォーマンス向上策も存在する。CSSのcontent-visibility: autoプロパティは、画面外にある要素のレンダリングをブラウザにスキップさせる指示を与えるため、仮想化と組み合わせることで描画コストをさらに削減できる。スクロールイベントは非常に高頻度で発生するため、requestAnimationFrameを使ってイベント処理の頻度を調整する「デバウンス」処理を導入すると、ブラウザの負荷を軽減できる。また、各セル内で複雑な計算やフォーマット処理を行う場合は、その結果をキャッシュ(メモ化)して、不要な再計算を避けるべきである。ReactのkeyプロパティやAngularのtrackBy関数を正しく利用し、フレームワークが変更された要素を効率的に識別できるようにすることも、パフォーマンス維持には不可欠である。

結論として、数千から数十万行規模の大量データを扱うデータグリッドを構築する際には、特定のフレームワークの最適化だけに依存するのではなく、データ処理と仮想化のロジックを「フレームワーク非依存のコア」に集約し、各フレームワークのアダプターがそのコアからの情報に基づいて「可視領域のみ」を効率的にレンダリングするアーキテクチャを採用することが非常に有効である。このアプローチにより、データ量に左右されない、高速で滑らかなユーザー体験を提供することが可能となる。このようなカスタムコアを構築することは、既存の汎用ライブラリでは実現が難しいような、極限のパフォーマンス要件や特殊なカスタマイズが必要な場合に特に大きな利点をもたらす。さらに、行の高さが動的に変わる可変行高のグリッドや、サーバーからデータを少しずつ読み込むサーバーサイドのデータ連携といった、より複雑なシナリオにもこのアーキテクチャは柔軟に対応できる。

関連コンテンツ

関連IT用語

関連ITニュース