1000コンポーネントでの`@reference` + `@apply`メモリ・パフォーマンス検証テストを追加
Vue統合テストに1000コンポーネントを使ったシナリオを追加し、@referenceと@applyの組み合わせによるOOM問題やメモリリークが現バージョンで発生しないことを確認・継続保証する仕組みを整備しました。
背景
Discussion #16429では、複数のVueコンポーネントが@referenceでCSSファイルを参照しつつ@applyを使用する構成において、OOM(Out of Memory)やCSSサイズの肥大化が報告されていました。これらの報告は実際のプロダクション規模での利用を想定した懸念であり、単純なユニットテストでは検証しきれない問題です。
PR作成時点では現行コードでOOMを再現できなかったものの、問題が再発しないことを保証する自動テストが存在しなかったため、今後のリグレッション検出を目的として本テストが追加されました。
技術的な変更
integrations/vite/vue.test.tsに、1000コンポーネントを生成してビルドおよびHMRを検証する統合テストケースが追加されました。
コンポーネント生成ロジックは、Array.fromとObject.fromEntriesを組み合わせてファイルシステムのエントリを動的に構築します。各コンポーネントは以下の構造を持ちます:
const VUE_COMPONENT_COUNT = 1_000
let vueComponentsWithReferences = Object.fromEntries(
Array.from({ length: VUE_COMPONENT_COUNT }, (_, idx) => [
`src/components/Component${idx}.vue`,
html`
<template>
<div class="content-['component-${idx}']">Component ${idx}</div>
</template>
<style>
@reference '../main.css';
.component-${idx} {
@apply text-red-500;
}
</style>
`,
]),
)
各コンポーネントは以下の3つの要素を組み合わせています:
-
content-['component-N']というコンポーネント固有のユーティリティクラス(JITで個別生成される) -
@reference '../main.css'によるメインスタイルシートへの参照 -
@apply text-red-500によるユーティリティの展開
さらに、1000コンポーネントを全てインポートしてマウントするApp.vue相当のエントリポイントも動的生成され、実際のビルドパイプライン全体を通じた検証が行われます。
ビルド結果として確認されたパフォーマンスは以下の通りです:
-
@reference+@applyあり:ビルド時間 3.17s、CSS出力 106.65 kB -
@reference+@applyなし:ビルド時間 1.97s、CSS出力 106.61 kB
1000ファイルに@referenceと@applyを追加することで約1.2秒の増加が確認されましたが、CSSサイズの肥大化は発生していません。
設計判断
コンポーネント数を1000に固定する設計は、OOM報告が多数コンポーネントを持つプロジェクトで発生していたという背景に基づいています。単一コンポーネントのテストでは問題を再現できないため、実際の規模感を模倣した数値が採用されました。
また、各コンポーネントが固有のクラス名(component-N)を持つ構造にすることで、CSSの重複排除や誤った最適化によってテストが形骸化することを防いでいます。すべてのコンポーネントで同一のクラスを使い回す設計では、メモリやCSSサイズの増大を適切に検出できないためです。
HMRの検証についても言及されており、Vite開発サーバーを起動して1000回のファイル変更を施してもメモリが増加し続けないことをPR作成者が手動で確認しています。テストコードとして自動化されているのはプロダクションビルドのアサーションですが、HMR動作の健全性も本PRの検証スコープに含まれています。
まとめ
このテストは、報告されたOOMやCSSサイズ肥大化の問題が現行コードで発生しないことを確認するとともに、将来の変更でリグレッションが起きた際に即座に検出できる安全網として機能します。@referenceと@applyを多用する実際のVueプロジェクト構成をそのまま模倣したテストシナリオが整備されたことで、パフォーマンス特性の変化を継続的に追跡できるようになります。