mcpField
T
tanstack-vue-virtual-skilld

tanstack-vue-virtual-skilld

github.com/skilld-dev開発184125 回インストール3 週間前に更新
リポジトリ

tanstack-vue-virtual-skilld について

大規模または動的なコレクションを扱う Vue 開発者向けの、ヘッドレスなリスト・グリッド仮想化アダプターです。Vue のリアクティビティを virtual-core の Virtualizer に接続し、ウィンドウスクロールや末尾固定のフィードにも対応します。

主な機能

  1. 01リアクティブな useVirtualizer と useWindowVirtualizer
  2. 02リストとグリッドの仮想化
  3. 03動的な項目サイズの計測
  4. 04末尾固定フィードとスクロール復元

活用シーン

  1. 01大規模リストの DOM 項目数削減
  2. 02末尾に固定されるチャットフィードの構築
  3. 03画面遷移後のスクロール位置復元

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/skilld-dev/vue-ecosystem-skills --skill tanstack-vue-virtual-skilld

インストール後、Claude Code で/tanstack-vue-virtual-skilldを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
chat-end-anchoring.md
patterns.md
virtualizer-api.md
vue-adapter.md

詳細

@tanstack/vue-virtual

Vue 向けのヘッドレスなリスト/グリッドの仮想化機能です。アダプターは薄いレイヤーで、useVirtualizer と useWindowVirtualizer はコアの Virtualizer を構築して Vue のリアクティビティに接続し、Ref として返します。以下のすべてのオプションとメソッドは @tanstack/virtual-core に由来し、アダプターから再エクスポートされています(src/index.ts:21)。

バージョン情報

  • 対象:@tanstack/[email protected](package.json:3)。
  • 固定依存関係:@tanstack/[email protected](package.json:52)。
  • ピア依存関係:vue ^2.7.0 || ^3.0.0(package.json:59)。
  • アダプター API は 3.13.x を通じて安定しています。リリース 3.13.25 から 3.13.39 では virtual-core のバージョンのみが更新されています(vue-virtual の変更履歴)。
  • インストール:npm install @tanstack/vue-virtual(インストールガイド)。

クイックスタート:高さが可変の行

公式の Vue の動的な例に沿った標準的なパターンです。ブロックの変換によって、描画されたブロックは最初の項目の start に配置され、各項目はブロック内で通常どおりに配置されます。

<script setup lang="ts">
import { computed, onMounted, onUpdated, ref, shallowRef } from 'vue'
import { useVirtualizer } from '@tanstack/vue-virtual'

const parentRef = ref<HTMLElement | null>(null)
const rows = Array.from({ length: 10000 }, (_, i) => `Row ${i}`)

const rowVirtualizer = useVirtualizer({
  count: rows.length,
  getScrollElement: () => parentRef.value,
  estimateSize: () => 55,
  overscan: 5,
})

const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())
const totalSize = computed(() => rowVirtualizer.value.getTotalSize())

const itemEls = shallowRef<HTMLElement[]>([])
function measureAll() {
  rowVirtualizer.value.measureElement(null) // prune disconnected nodes
  itemEls.value.forEach((el) => el && rowVirtualizer.value.measureElement(el))
}
onMounted(measureAll)
onUpdated(measureAll)
</script>

<template>
  <div ref="parentRef" style="height: 400px; overflow-y: auto; contain: strict">
    <div :style="{ height: `${totalSize}px`, position: 'relative', width: '100%' }">
      <div
        :style="{
          position: 'absolute',
          top: 0,
          left: 0,
          width: '100%',
          transform: `translateY(${virtualRows[0]?.start ?? 0}px)`,
        }"
      >
        <div
          v-for="virtualRow in virtualRows"
          :key="virtualRow.key"
          :data-index="virtualRow.index"
          ref="itemEls"
        >
          {{ rows[virtualRow.index] }}
        </div>
      </div>
    </div>
  </div>
</template>

スクロールコンテナーには、上限のある高さと overflow: auto が必要です。内側のサイズ調整用 div には getTotalSize() を設定します。

ウィンドウバーチャライザー

useWindowVirtualizer はブラウザーのウィンドウに合わせてスクロールします。getScrollElement(SSR セーフ)と initialOffset はあらかじめ設定されるため、どちらも渡さないでください(vue-virtual のドキュメント、src/index.ts:104-114)。リストがほかのコンテンツの下にある場合は、そのオフセットを scrollMargin に計測します。

const parentOffset = ref(0)
onMounted(() => {
  parentOffset.value = parentRef.value?.offsetTop ?? 0
})

const rowVirtualizer = useWindowVirtualizer(
  computed(() => ({
    count: rows.length,
    estimateSize: () => 45,
    scrollMargin: parentOffset.value,
  })),
)

パターンの出典:公式のウィンドウの例。

Vue アダプターのルール(src/index.ts で確認済み)

  • Virtualizer の生のインスタンスではなく、Ref<Virtualizer> を返します。スクリプトでは .value を使用し、テンプレートでは自動的にアンラップされます(src/index.ts:30,69)。
  • オプションは MaybeRef を受け付けます。通常のオブジェクトは一度だけ適用されます。count、overscan、enabled などをリアクティブに更新するには、オプション全体を ref または computed にして渡してください。アダプターは () => unref(options) を監視し、変更時に setOptions を呼び出します(src/index.ts:48-65)。
  • getScrollElement() は immediate: true で監視されます。セットアップ時にまだ null のテンプレート ref も、マウント時に検出されます(src/index.ts:36-46)。
  • onChange はラップされています。アダプターはユーザーのコールバックより先に、内部の shallowRef で triggerRef を呼び出します。そのため、computed(() => rowVirtualizer.value.getVirtualItems()) はスクロールやサイズ変更時に再評価されます(src/index.ts:53-56)。
  • オブザーバーは onScopeDispose でクリーンアップされます(src/index.ts:67)。
  • measureElement で計測する項目には、デフォルトで data-index のインデックス属性が必要です(indexAttribute で変更できます)。
  • アダプターがあらかじめ設定するため、observeElementRect、observeElementOffset、scrollToFn は渡さないでください(src/index.ts:83-90)。

詳細:Vue アダプター API。

API リファレンス

オプションの完全な表、インスタンスメソッド、VirtualItem の形式:Virtualizer API。

useVirtualizer の必須オプション:count、getScrollElement、estimateSize(virtualizer のドキュメント)。

virtual-core 3.14.0 以降の新機能

vue-virtual 3.13.39 には core 3.17.11 が含まれています。以下は core のマイナーバージョンで追加された機能です(virtual-core の変更履歴)。

  • 3.16.0:チャット、ログ、逆方向フィード向けの末尾アンカー モード。anchorTo: 'end'、followOnAppend、scrollEndThreshold に加え、scrollToEnd()、isAtEnd()、getDistanceFromEnd() が追加されました。チャットと末尾アンカーを参照してください。
  • 3.15.0:initialMeasurementsCache と initialOffset を使ったスクロール復元の往復に対応する takeSnapshot()。
  • 3.15.0:後方スクロール中はデフォルトでスクロール調整による補正をスキップするようになりました(「上にスクロールすると項目が跳ねる」問題を修正)。上書きするには、オプションではないインスタンスプロパティ shouldAdjustScrollPositionOnItemSizeChange を割り当てます。
  • 3.15.0:iOS Safari のモーメンタムスクロール処理がデフォルトで有効になりました。タッチ中またはバウンス中は、スクロール補正の書き込みが遅延されます。
  • 3.17.0:useCachedMeasurements により、リストが非表示でも計測値を保持します。
  • 3.15.0 より前から利用できるオプション:laneAssignmentMode を指定した lanes(3.14.0)、gap、indexAttribute、initialMeasurementsCache、isRtl。

ベストプラクティス

  • 動的な項目では大きめに見積もります。初期位置とスクロールバーを安定させるため、estimateSize は想定される最大サイズに近い値に設定します(virtualizer のドキュメント)。
  • 項目ごとの絶対位置指定よりも、ブロック単位の変換(クイックスタートの例を参照)を優先します。スムーズな scrollToIndex は、対象から遠い項目の計測をスキップします。ブロック単位の変換を使うと、描画ブロック内の整合性を保てます(virtualizer のドキュメント)。
  • データの並べ替え、フィルタリング、先頭への項目追加、ストリーミングを行う場合は、安定した getItemKey(インデックスではなく行 ID)を指定します。インデックスをキーにすると、末尾アンカー付きチャットとスクロール復元で問題が発生します(チャットのドキュメント)。
  • 共通のスクロールコンテナー内で項目を絶対配置する場合は、項目の開始位置から scrollMargin を差し引きます(virtualizer のドキュメント)。
  • 間隔を getTotalSize() に反映させるには、CSS のマージンではなく gap オプションを使います。
  • count の変更時には計測キャッシュが無効化され、getTotalSize() が自動的に更新されます(3.13.13 で修正、変更履歴)。Vue では、リアクティブなデータを引き続き computed オプション経由で渡してください。
  • 効果があると確認されていない限り、useAnimationFrameWithResizeObserver はオフのままにします。有効にすると計測が 1 フレーム遅延されます(virtualizer のドキュメント)。
  • 計測値を保持するには、オブザーバーをアンマウントするのではなく、enabled: false で一時停止します。

masonry、パディング、固定ヘッダー、スクロール復元、非表示リストのレシピ:パターン。

v2 からの移行:useVirtual はなくなりました。v3 では useVirtualizer、size ではなく count オプション、parentRef ではなく getScrollElement、および data-index を持つ measureElement の ref を使います(vue-virtual のドキュメント)。

参考資料

  • Vue アダプター API:シグネチャ、リアクティビティの契約、SSR の動作。
  • Virtualizer API:すべてのオプション、デフォルト値、インスタンスメソッド、VirtualItem。
  • パターン:masonry、スクロールマージン、固定範囲、スクロール復元、非表示リスト。
  • チャットと末尾アンカー:Vue での anchorTo: 'end' を使ったチャットフィード。

コメント