この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:7
ページ更新者:guest
更新日時:2026-06-11 07:29:05

タイトル: for 繰り返し処理
SEOタイトル: Vue.js v-for 繰り返し処理完全ガイド

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>v-for</strong> は Vue.js の繰り返しディレクティブ。配列・オブジェクト・数値・文字列に対応</li><li>基本構文: <code>&lt;li v-for=&quot;item in items&quot; :key=&quot;item.id&quot;&gt;</code></li><li>インデックスも取れる: <code>v-for=&quot;(item, index) in items&quot;</code></li><li><strong>:key 必須</strong>。一意な ID を渡さないと再レンダリングで挙動が崩れる</li><li><strong>v-for と v-if の同一要素</strong>は警告 → <code>&lt;template v-for&gt;</code> で囲んで分離</li></ul></td></tr></table>
<h2>v-for の基本</h2>

<p>Vue.js の <strong>v-for</strong> ディレクティブは、配列やオブジェクトなどの反復可能なデータを使って DOM 要素を繰り返し描画します。Vue 2 / Vue 3 ともに最も使用頻度の高いディレクティブの一つです。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;template&gt;
  &lt;ul&gt;
    &lt;li v-for=&quot;fruit in fruits&quot; :key=&quot;fruit&quot;&gt;{{ fruit }}&lt;/li&gt;
  &lt;/ul&gt;
&lt;/template&gt;

&lt;script setup&gt;
const fruits = [&apos;Apple&apos;, &apos;Banana&apos;, &apos;Cherry&apos;];
&lt;/script&gt;</code></pre>
<p>レンダリング結果:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;ul&gt;
  &lt;li&gt;Apple&lt;/li&gt;
  &lt;li&gt;Banana&lt;/li&gt;
  &lt;li&gt;Cherry&lt;/li&gt;
&lt;/ul&gt;</code></pre>
<h2>インデックスを取得</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;ul&gt;
  &lt;li v-for=&quot;(fruit, index) in fruits&quot; :key=&quot;index&quot;&gt;
    {{ index }}: {{ fruit }}
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!--
0: Apple
1: Banana
2: Cherry
--&gt;</code></pre>
<h2>オブジェクトの反復</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;script setup&gt;
const user = {
  name: &apos;Alice&apos;,
  age: 30,
  job: &apos;Developer&apos;,
};
&lt;/script&gt;

&lt;template&gt;
  &lt;!-- 値のみ --&gt;
  &lt;ul&gt;
    &lt;li v-for=&quot;value in user&quot;&gt;{{ value }}&lt;/li&gt;
  &lt;/ul&gt;

  &lt;!-- 値, キー --&gt;
  &lt;ul&gt;
    &lt;li v-for=&quot;(value, key) in user&quot;&gt;{{ key }}: {{ value }}&lt;/li&gt;
  &lt;/ul&gt;

  &lt;!-- 値, キー, インデックス --&gt;
  &lt;ul&gt;
    &lt;li v-for=&quot;(value, key, index) in user&quot; :key=&quot;key&quot;&gt;
      [{{ index }}] {{ key }} = {{ value }}
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/template&gt;</code></pre>
<h2>数値範囲・文字列の反復</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;!-- 1〜10 --&gt;
&lt;span v-for=&quot;n in 10&quot; :key=&quot;n&quot;&gt;{{ n }} &lt;/span&gt;
&lt;!-- 1 2 3 4 5 6 7 8 9 10 --&gt;

&lt;!-- 文字列の各文字 --&gt;
&lt;span v-for=&quot;ch in &apos;Hello&apos;&quot; :key=&quot;ch&quot;&gt;{{ ch }}.&lt;/span&gt;
&lt;!-- H. e. l. l. o. --&gt;</code></pre>
<h2>:key の重要性</h2>

<p>Vue は <strong>:key</strong> を使ってリスト内の各要素を識別します。これがないと「リスト要素が並び替わったとき」「真ん中に挿入されたとき」に Vue が DOM 再利用に失敗し、入力欄の値がズレる・アニメーションが破綻するといった問題が起きます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;!-- ❌ 不適: 配列インデックスを key にすると、insert/delete で挙動が崩れる --&gt;
&lt;li v-for=&quot;(item, idx) in items&quot; :key=&quot;idx&quot;&gt;

&lt;!-- ✅ 正解: 一意な ID を使う --&gt;
&lt;li v-for=&quot;item in items&quot; :key=&quot;item.id&quot;&gt;

&lt;!-- ✅ 文字列リストで ID が無いなら値自身でも OK (重複しない前提) --&gt;
&lt;li v-for=&quot;tag in tags&quot; :key=&quot;tag&quot;&gt;</code></pre>
<h2>v-for と v-if の組み合わせ警告</h2>

<p><strong>同一要素で v-for と v-if を使うのは非推奨</strong>です。Vue 3 では v-if が優先され、v-for で展開された個別要素を見ないため意図しない挙動になります。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;!-- ❌ NG: 同一要素 --&gt;
&lt;li v-for=&quot;todo in todos&quot; v-if=&quot;!todo.done&quot; :key=&quot;todo.id&quot;&gt;
  {{ todo.text }}
&lt;/li&gt;

&lt;!-- ✅ template でラップ (Vue 3) --&gt;
&lt;template v-for=&quot;todo in todos&quot; :key=&quot;todo.id&quot;&gt;
  &lt;li v-if=&quot;!todo.done&quot;&gt;{{ todo.text }}&lt;/li&gt;
&lt;/template&gt;

&lt;!-- ✅ computed でフィルタ (推奨) --&gt;
&lt;li v-for=&quot;todo in activeTodos&quot; :key=&quot;todo.id&quot;&gt;{{ todo.text }}&lt;/li&gt;

&lt;script setup&gt;
import { computed } from &apos;vue&apos;;
const todos = ref([...]);
const activeTodos = computed(() =&gt; todos.value.filter(t =&gt; !t.done));
&lt;/script&gt;</code></pre>
<h2>ネストした v-for</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;script setup&gt;
const categories = [
  { name: &apos;Fruit&apos;,  items: [&apos;Apple&apos;, &apos;Banana&apos;] },
  { name: &apos;Vegetable&apos;, items: [&apos;Carrot&apos;, &apos;Potato&apos;] },
];
&lt;/script&gt;

&lt;template&gt;
  &lt;div v-for=&quot;cat in categories&quot; :key=&quot;cat.name&quot;&gt;
    &lt;h3&gt;{{ cat.name }}&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li v-for=&quot;item in cat.items&quot; :key=&quot;item&quot;&gt;{{ item }}&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
<h2>複数要素をラップ (<template v-for>)</h2>

<p>1 つの v-for で<strong>複数のタグ</strong>を出力したい場合は <code>&lt;template&gt;</code> を使います (template タグは DOM に出力されません):</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;dl&gt;
  &lt;template v-for=&quot;user in users&quot; :key=&quot;user.id&quot;&gt;
    &lt;dt&gt;{{ user.name }}&lt;/dt&gt;
    &lt;dd&gt;{{ user.email }}&lt;/dd&gt;
  &lt;/template&gt;
&lt;/dl&gt;</code></pre>
<h2>computed でフィルタ・ソート</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">&lt;script setup&gt;
import { ref, computed } from &apos;vue&apos;;

const users = ref([
  { id: 1, name: &apos;Alice&apos;, age: 30 },
  { id: 2, name: &apos;Bob&apos;,   age: 25 },
  { id: 3, name: &apos;Carol&apos;, age: 35 },
]);

const sortedUsers = computed(() =&gt;
  [...users.value].sort((a, b) =&gt; a.age - b.age)
);

const adultUsers = computed(() =&gt;
  users.value.filter(u =&gt; u.age &gt;= 30)
);
&lt;/script&gt;

&lt;template&gt;
  &lt;h3&gt;Sorted by age&lt;/h3&gt;
  &lt;ul&gt;
    &lt;li v-for=&quot;u in sortedUsers&quot; :key=&quot;u.id&quot;&gt;{{ u.name }} ({{ u.age }})&lt;/li&gt;
  &lt;/ul&gt;
&lt;/template&gt;</code></pre>
<p>配列を <strong>computed の中でコピー</strong> (<code>[...arr]</code>) してから sort することで、reactive の元配列を破壊しません。</p>

<h2>Vue 2 と Vue 3 の v-for の違い</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3e0"><th>項目</th><th>Vue 2</th><th>Vue 3</th></tr></thead>
<tbody>
<tr><td>v-for と v-if の優先度</td><td>v-for が優先</td><td>v-if が優先</td></tr>
<tr><td>:key の必須度</td><td>推奨</td><td>必須 (デフォルト警告)</td></tr>
<tr><td>$index, $key (暗黙変数)</td><td>非推奨</td><td>削除</td></tr>
<tr><td>v-bind スコープ</td><td>親と同じ</td><td>反復子も参照可</td></tr>
</tbody>
</table>

<h2>非破壊的に配列を変更</h2>

<p>Vue 2 の頃は <code>Vue.set()</code> や <code>this.$set</code> が必要なケースがありましたが、Vue 3 は Proxy ベースで<strong>配列のあらゆる変更を検知</strong>します:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">import { ref } from &apos;vue&apos;;

const items = ref([1, 2, 3]);

// すべて自動でリアクティブ
items.value.push(4);
items.value.pop();
items.value.splice(1, 0, 99);
items.value[0] = 100;                   // ← Vue 3 では検知される
items.value.sort();
items.value = items.value.filter(x =&gt; x &gt; 1);</code></pre>
<h2>FAQ</h2>

<p><strong>Q: :key にオブジェクトを渡しても良いですか</strong><br />
A: ダメです。<strong>プリミティブ (文字列/数値)</strong> を渡してください。オブジェクトはハッシュキーが一意でなく Warning が出ます。</p>

<p><strong>Q: v-for をパフォーマンス上の理由で避けたい</strong><br />
A: 仮想スクロール (vue-virtual-scroller) を使うと、1 万件でも 50 件分だけ描画するので高速です。</p>

<p><strong>Q: Vue 2 の <code>this.$set(this.items, idx, val)</code> はもう不要</strong><br />
A: Vue 3 では完全に不要です。直接代入で OK。</p>