3.

Vue.js v-for 繰り返し処理完全ガイド

編集
この記事の要点
  • v-for は Vue.js の繰り返しディレクティブ。配列・オブジェクト・数値・文字列に対応
  • 基本構文: <li v-for="item in items" :key="item.id">
  • インデックスも取れる: v-for="(item, index) in items"
  • :key 必須。一意な ID を渡さないと再レンダリングで挙動が崩れる
  • v-for と v-if の同一要素は警告 → <template v-for> で囲んで分離

v-for の基本

Vue.js の v-for ディレクティブは、配列やオブジェクトなどの反復可能なデータを使って DOM 要素を繰り返し描画します。Vue 2 / Vue 3 ともに最も使用頻度の高いディレクティブの一つです。

<template>
  <ul>
    <li v-for="fruit in fruits" :key="fruit">{{ fruit }}</li>
  </ul>
</template>

<script setup>
const fruits = ['Apple', 'Banana', 'Cherry'];
</script>

レンダリング結果:

<ul>
  <li>Apple</li>
  <li>Banana</li>
  <li>Cherry</li>
</ul>

インデックスを取得

<ul>
  <li v-for="(fruit, index) in fruits" :key="index">
    {{ index }}: {{ fruit }}
  </li>
</ul>

<!--
0: Apple
1: Banana
2: Cherry
-->

オブジェクトの反復

<script setup>
const user = {
  name: 'Alice',
  age: 30,
  job: 'Developer',
};
</script>

<template>
  <!-- 値のみ -->
  <ul>
    <li v-for="value in user">{{ value }}</li>
  </ul>

  <!-- 値, キー -->
  <ul>
    <li v-for="(value, key) in user">{{ key }}: {{ value }}</li>
  </ul>

  <!-- 値, キー, インデックス -->
  <ul>
    <li v-for="(value, key, index) in user" :key="key">
      [{{ index }}] {{ key }} = {{ value }}
    </li>
  </ul>
</template>

数値範囲・文字列の反復

<!-- 1〜10 -->
<span v-for="n in 10" :key="n">{{ n }} </span>
<!-- 1 2 3 4 5 6 7 8 9 10 -->

<!-- 文字列の各文字 -->
<span v-for="ch in 'Hello'" :key="ch">{{ ch }}.</span>
<!-- H. e. l. l. o. -->

:key の重要性

Vue は :key を使ってリスト内の各要素を識別します。これがないと「リスト要素が並び替わったとき」「真ん中に挿入されたとき」に Vue が DOM 再利用に失敗し、入力欄の値がズレる・アニメーションが破綻するといった問題が起きます。

<!-- ❌ 不適: 配列インデックスを key にすると、insert/delete で挙動が崩れる -->
<li v-for="(item, idx) in items" :key="idx">

<!-- ✅ 正解: 一意な ID を使う -->
<li v-for="item in items" :key="item.id">

<!-- ✅ 文字列リストで ID が無いなら値自身でも OK (重複しない前提) -->
<li v-for="tag in tags" :key="tag">

v-for と v-if の組み合わせ警告

同一要素で v-for と v-if を使うのは非推奨です。Vue 3 では v-if が優先され、v-for で展開された個別要素を見ないため意図しない挙動になります。

<!-- ❌ NG: 同一要素 -->
<li v-for="todo in todos" v-if="!todo.done" :key="todo.id">
  {{ todo.text }}
</li>

<!-- ✅ template でラップ (Vue 3) -->
<template v-for="todo in todos" :key="todo.id">
  <li v-if="!todo.done">{{ todo.text }}</li>
</template>

<!-- ✅ computed でフィルタ (推奨) -->
<li v-for="todo in activeTodos" :key="todo.id">{{ todo.text }}</li>

<script setup>
import { computed } from 'vue';
const todos = ref([...]);
const activeTodos = computed(() => todos.value.filter(t => !t.done));
</script>

ネストした v-for

<script setup>
const categories = [
  { name: 'Fruit',  items: ['Apple', 'Banana'] },
  { name: 'Vegetable', items: ['Carrot', 'Potato'] },
];
</script>

<template>
  <div v-for="cat in categories" :key="cat.name">
    <h3>{{ cat.name }}</h3>
    <ul>
      <li v-for="item in cat.items" :key="item">{{ item }}</li>
    </ul>
  </div>
</template>

複数要素をラップ (<template v-for>)

1 つの v-for で複数のタグを出力したい場合は <template> を使います (template タグは DOM に出力されません):

<dl>
  <template v-for="user in users" :key="user.id">
    <dt>{{ user.name }}</dt>
    <dd>{{ user.email }}</dd>
  </template>
</dl>

computed でフィルタ・ソート

<script setup>
import { ref, computed } from 'vue';

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

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

const adultUsers = computed(() =>
  users.value.filter(u => u.age >= 30)
);
</script>

<template>
  <h3>Sorted by age</h3>
  <ul>
    <li v-for="u in sortedUsers" :key="u.id">{{ u.name }} ({{ u.age }})</li>
  </ul>
</template>

配列を computed の中でコピー ([...arr]) してから sort することで、reactive の元配列を破壊しません。

Vue 2 と Vue 3 の v-for の違い

項目Vue 2Vue 3
v-for と v-if の優先度v-for が優先v-if が優先
:key の必須度推奨必須 (デフォルト警告)
$index, $key (暗黙変数)非推奨削除
v-bind スコープ親と同じ反復子も参照可

非破壊的に配列を変更

Vue 2 の頃は Vue.set()this.$set が必要なケースがありましたが、Vue 3 は Proxy ベースで配列のあらゆる変更を検知します:

import { ref } from 'vue';

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 => x > 1);

FAQ

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

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

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

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. インストール(ファイルのダウンロード)
  2. npmを使用したプロジェクトの作成
  3. for 繰り返し処理
  4. ifの条件分岐とtemplateを用いたグループ化
  5. クリック時のイベント処理(on:click)
  6. modelとdata フォーム入力値とDOMへの即時反映
  7. computed(算出プロパティ)と使い方とdataとの違い
  8. ライフサイクルフック(created / mounted / updated / destroyedの使い方)
  9. $nextTickの使い方(ライフサイクルフック)
  10. メソッドの定義方法
  11. エラー一覧
  12. ルーティング設定
  13. aリンクの貼り方と動的URLの作成
  14. Mixinを利用した共通処理の記述方法
  15. v-bindによるデータ連携
  16. ヘッダー/フッターの共通コンポーネント
  17. ナビゲーションの現在ページをハイライトする方法
  18. 画面サイズの取得方法

最近更新/作成されたページ