0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue 3の`v-for`で一覧を表示する。`key`と`index`の役割を整理してみた

0
Last updated at Posted at 2026-07-26

今回はVue 3のv-forを使って、配列のデータを繰り返し画面へ表示しました。
画像解析結果や写真一覧を作るときにも使えそうなので、箇条書きとの違い、key、配列のindexまでまとめた学習メモです。

まだ途中段階なので、勘違いなどあれば教えていただけるとうれしいです。

今日やったこと

v-forで配列のデータを1件ずつ取り出し、同じ形の要素を繰り返して表示しました。

v-for="flower in flowers"では、flowersが元になる配列、flowerが繰り返し中の1件のデータです。Vueでは、配列の要素数に合わせて要素をレンダリングできます。Vue.js公式ドキュメント

<script setup>
import { ref } from 'vue'

// 一覧へ表示する花の名前
const flowers = ref(['Rose', 'Lily', 'Tulip', 'Daisy'])
</script>

<template>
  <!-- ul の中で li を繰り返すため、箇条書きになる -->
  <ul>
    <!-- flowers の要素ごとに li が1つ作られる -->
    <li v-for="flower in flowers" :key="flower">
      {{ flower }}
    </li>
  </ul>

  <br />

  <!-- div も繰り返せるが、箇条書きの記号は付かない -->
  <div v-for="flower in flowers" :key="flower">
    {{ flower }}
  </div>
</template>

image.png

このコードでは、flowersに4件のデータがあるため、花の名前が4回ずつ表示されます。

ここで分かったのは、v-forと箇条書きの表示は別の話だということです。

  • v-forは、配列のデータ数に合わせて要素を繰り返す
  • ulliは、箇条書きとして表示するHTML構造を作る
  • divは、箇条書きではない通常のブロック要素として表示する

つまり、v-forが「・」を付けたり消したりしているわけではありません。ulの中にliを置くから箇条書きになり、divを使うと通常の縦並びになります。

キャラクター 「`v-for`はデータの数だけ並べる係で、箇条書きにするかカードにするかはHTML側で決めるんだね。」

ゲームのアイテム一覧にたとえると、v-forは持っているアイテムの数だけ表示欄を用意する仕組みに近そうです。文字だけの一覧にするか、画像付きカードにするかは、lidiv、CSSの役割になります。

つまずいたところ

一覧を表示できても、keyが何をしているのかは最初は見えにくかったです。

keyはVueが項目を見分ける目印

v-forで繰り返す要素には、基本的に:keyを付けます。keyはVueが「画面上のこの要素は、配列内のどのデータなのか」を見分けるために使う目印です。

:key:は、v-bindの省略記法です。:key="flower"は、繰り返し中のflowerの値をkeyとして渡しています。

<script setup>
import { ref } from 'vue'

// 花の名前を配列で管理する
const flowers = ref(['Rose', 'Lily', 'Tulip', 'Daisy'])
</script>

<template>
  <!-- 配列の先頭にある要素を削除する -->
  <button @click="flowers.shift()">先頭を削除</button>

  <ul>
    <!-- 花の名前を、項目を見分ける key として使う -->
    <li v-for="flower in flowers" :key="flower">
      <!-- 各行に入力欄を置く -->
      <input type="text" />

      {{ flower }}
    </li>
  </ul>
</template>

key.gif

flowers.shift()は、配列の先頭のデータを削除するメソッドです。この例では、ボタンを押すと最初のRoseが消えます。

一覧が増減したり順番が変わったりするとき、Vueはできるだけ既存のDOM要素を再利用します。入力欄のように要素ごとの状態を持つ部品がある場合、keyがないとデータと画面上の要素の対応が分かりにくくなる可能性があります。

Vue公式でも、v-forでは一意なkeyを指定することが推奨されています。Vue.js公式ドキュメント

キャラクター 「`key`は画面に表示する名前ではなくて、Vueが“前のどの項目か”を判断するための名札みたいなものなんだね。」

表示名が重複する場合

keyには、一覧内で重複しない値が必要です。花の名前が必ず違うなら:key="flower"でもよさそうですが、同じRoseが2件あれば、同じkeyになってしまいます。

その場合は、idを持つオブジェクトの配列にします。

<script setup>
import { ref } from 'vue'

// name が同じでも、id はそれぞれ異なる値にする
const flowers = ref([
  { id: 1, name: 'Rose' },
  { id: 2, name: 'Rose' },
  { id: 3, name: 'Tulip' },
  { id: 4, name: 'Daisy' }
])
</script>

<template>
  <!-- 先頭のデータを削除する -->
  <button @click="flowers.shift()">先頭を削除</button>

  <ul>
    <!-- 重複しない id を key として使う -->
    <li v-for="flower in flowers" :key="flower.id">
      <input type="text" />

      <!-- 画面には利用者に見せる名前を表示する -->
      {{ flower.name }}
    </li>
  </ul>
</template>

key_2.gif

この書き方なら、画面には同じRoseが2件あっても、Vueはid: 1id: 2を別のデータとして扱えます。

画像解析結果の一覧では、ファイル名が重複する可能性もありそうです。画面へ表示する名前とは別に、解析結果IDや画像IDのような一意の値をkeyに使うほうがよさそうです。

今の理解(メモ)

まだふわっとしていますが、v-forはデータを繰り返し表示し、keyはそのデータと画面上の要素を対応付ける仕組みだと理解しています。

  • v-for="flower in flowers"は、flowersから1件ずつ取り出して表示する
  • flowerは、繰り返し中の1件のデータを入れる変数名
  • ulliを使うか、divを使うかで、画面の構造や見た目が変わる
  • :keyは、Vueが各項目を一意に識別するための値
  • keyには、表示順ではなく、データ固有で変わりにくいIDを使う
  • 同じ値がある配列では、値そのものをkeyにせず、idを持つオブジェクトを使う

ゲームのパーティー一覧で考えると、keyは各キャラクターの識別番号に近そうです。並び順が変わっても誰なのかは変わらないため、名前や現在の順番よりも、固有のIDを使うほうが合っています。

配列のindexを取得する

v-forでは、各データだけでなく、配列内で何番目にあるかを表すindexも取得できます。

書き方は、v-for="(flower, index) in flowers"です。indexは0から始まるため、最初の要素は0、2つ目は1になります。Vue.js公式ドキュメント

<script setup>
import { ref } from 'vue'

// id と name を持つ花の一覧
const flowers = ref([
  { id: 1, name: 'Rose' },
  { id: 2, name: 'Rose' },
  { id: 3, name: 'Tulip' },
  { id: 4, name: 'Daisy' }
])
</script>

<template>
  <!-- 先頭のデータを削除する -->
  <button @click="flowers.shift()">先頭を削除</button>

  <ul>
    <!-- flower は1件のデータ、index は現在の並び順を表す -->
    <li v-for="(flower, index) in flowers" :key="flower.id">
      <input type="text" />

      <!-- index は0から始まる番号として表示される -->
      {{ flower.name }} ({{ index }})
    </li>
  </ul>
</template>

最初は、次のように表示されます。

Rose (0)
Rose (1)
Tulip (2)
Daisy (3)

先頭のRoseを削除すると、残ったデータのindexは詰め直されます。

Rose (0)
Tulip (1)
Daisy (2)

つまり、indexはデータ固有の番号ではなく、今の配列で何番目に並んでいるかを表す値です。要素を削除・追加・並び替えすると、indexは変わります。

そのため、indexは画面へ順番を表示するためには使えますが、:keyには基本的に使わないほうがよさそうです。

<!-- 避けたい書き方:削除・追加・並び替えで index が変わる -->
<li v-for="(flower, index) in flowers" :key="index">
  {{ flower.name }}
</li>

先頭のデータを削除すると、もともとindex1だったデータも0になります。keyが変わると、VueがどのDOM要素を再利用するかを正しく判断しにくくなり、特に入力欄やチェックボックスを含む一覧で意図しない表示につながる可能性があります。

<!-- こちらを使う:並び順が変わっても id は変わらない -->
<li v-for="(flower, index) in flowers" :key="flower.id">
  <input type="text" />
  {{ flower.name }} ({{ index }})
</li>
キャラクター 「`index`は今いる場所の番号で、`id`はそのデータ自身の番号。用途を分けると混ざりにくそうだね。」

ゲームのたとえでは、indexは現在のパーティーの並び順、idは各キャラクターに付いた識別番号のようなものです。隊列は変わってもキャラクター自体は変わらないので、keyにはidを使うほうが自然そうです。

分割代入で書く

オブジェクト配列では、分割代入を使ってidnameを取り出す書き方もできます。

<script setup>
import { ref } from 'vue'

// 花ごとに id と name を持たせる
const flowers = ref([
  { id: 1, name: 'Rose' },
  { id: 2, name: 'Rose' },
  { id: 3, name: 'Tulip' },
  { id: 4, name: 'Daisy' }
])
</script>

<template>
  <button @click="flowers.shift()">先頭を削除</button>

  <ul>
    <!-- flower から id と name を直接取り出し、index も受け取る -->
    <li v-for="({ id, name }, index) in flowers" :key="id">
      <input type="text" />
      {{ name }} ({{ index }})
    </li>
  </ul>
</template>

通常の書き方ではflower.idflower.nameと書きます。分割代入では、繰り返しの時点でidnameを取り出すため、テンプレート側ではそのままidnameと書けます。

  • 分割代入なし:v-for="(flower, index) in flowers"
  • 分割代入あり:v-for="({ id, name }, index) in flowers"

動きは同じなので、まずはflower.idの書き方を理解してから、短く書きたい場合に分割代入を使うのがよさそうです。

次にやりたいこと

次は、実際の一覧画面に近い形でv-forを試したいです。

  • id、画像名、解析日時、解析状態を持つ仮データを作り、画像解析結果のカードをv-forで並べる
  • indexを画面へ表示し、削除や並び替えで番号がどう変わるか確認する
  • 入力欄やチェックボックス付きの一覧で、:key="index":key="item.id"の違いを見比べる
  • template v-forを使い、1件のデータからタイトル・画像・説明文など複数の要素を表示する
  • v-forv-ifを同じ要素に書かず、検索条件で絞り込んだ配列をcomputedで用意してから表示する

今日の整理

v-forは一覧を作るための仕組みで、keyindexは役割が違うものとして整理しました。

用語・書き方 役割 利用イメージ 次に確認したいこと
v-for="item in items" 配列のデータを1件ずつ表示する 写真・検索結果・解析結果の一覧 カード形式での表示
ul + li 箇条書きのHTML構造を作る テキストの一覧、メニュー CSSで記号を調整する方法
div + v-for ブロック要素を繰り返す カード・グリッドの一覧 CSS Gridとの組み合わせ
:key="item.id" 各項目を一意に見分ける APIやデータベースの結果一覧 実際のデータのID
index 現在の配列内の順番を取得する 何件目かの表示、順位表示 並び替え時の番号変化
分割代入 オブジェクトの値を直接取り出す idnameを短く書く 複雑なデータでの読みやすさ

v-forv-ifを同じ要素に書くと、優先順位や変数の使える範囲が分かりにくくなります。Vue公式のスタイルガイドでも、同じ要素での併用は避け、一覧の絞り込みはcomputedで行うか、一覧全体の表示条件は親要素へ書く形が推奨されています。Vue.js公式スタイルガイド

たとえば、一覧全体を表示するかどうかは親要素で判断できます。

<script setup>
import { ref } from 'vue'

// 一覧表示を切り替えるための状態
const shouldShowFlowers = ref(true)

// 表示する花の一覧
const flowers = ref([
  { id: 1, name: 'Rose' },
  { id: 2, name: 'Lily' }
])
</script>

<template>
  <!-- 一覧全体の表示・非表示を親要素で切り替える -->
  <ul v-if="shouldShowFlowers">
    <!-- 子要素でデータを繰り返す -->
    <li v-for="flower in flowers" :key="flower.id">
      {{ flower.name }}
    </li>
  </ul>
</template>

おわりに

今回はv-forを使って、配列のデータを一覧として表示する方法を触りました。v-forはデータを並べるためのもの、ulliは箇条書きの形を作るもの、と分けて考えると混乱が少し減りました。

また、indexは現在の順番を表示するための値、keyはVueが各データを見分けるためのIDとして使うものだと整理できました。画像解析結果を一覧で扱うときにも、表示順とデータIDを混ぜないように意識したいです。

学習中の内容なので、勘違いや理解不足もあると思います。もし気になる点があれば教えてもらえるとうれしいです。

 注意事項
 本ブログに掲載している内容は、私個人の見解であり、
 所属する組織の立場や戦略、意見を代表するものではありません。
 あくまでエンジニアとしての経験や考えを発信していますのでご了承ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?