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?

今回はループのディレクティブである「v-for」について
Vueチュートリアルの問題を噛み砕きまくって
分かるレベルまで落とし込みました。

v-forは超簡単にいうと
ループ処理ができるディレクティブ

問題のコード(回答)

参考にしたサイト
VueチュートリアルSTEP7

こんな動作をします

1:new todoと書かれたテキストボックス内に記述
2:「Add ToDo」と書かれたボタンを押すと
3:初期値で入力されていた要素の下に追加される
4:xボタンを押すと消える

Image from Gyazo

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

// give each todo a unique id
let id = 0

const newTodo = ref('')
const todos = ref([
  { id: id++, text: 'Learn HTML' },
  { id: id++, text: 'Learn JavaScript' },
  { id: id++, text: 'Learn Vue' }
])

function addTodo() {
  todos.value.push({ id: id++, text: newTodo.value })
  newTodo.value = ''
}

function removeTodo(todo) {
  todos.value = todos.value.filter((t) => t !== todo)
}
</script>

<template>
  <form @submit.prevent="addTodo">
    <input v-model="newTodo" required placeholder="new todo">
    <button>Add Todo</button>
  </form>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.text }}
      <button @click="removeTodo(todo)">X</button>
    </li>
  </ul>
</template>

①大前提 Vueの構造をまずおさらい

SFC(単一ファイルコンポーネント) を構成する3つのブロックがある

script template style

<script setup> </script> ロジック担当
頭脳である、JavaScript(ロジック)を記述するところ

・function(関数)の定義やロジックの記述
・データの定義(ref reactive)
・インポート機能 import {ref} from 'vue'のように
必要な機能を外部から読み込む
・自動公開 setup属性をつけることで内で(.value)無しで記述可能に

<template> </template> 構造担当
HTML構造を記述するところだが、
単なるHTMLではなく、
JavaScriptの状態(データ)と見た目を繋ぐため Vue独自の構文が使える

例:
宣言的レンダリング(マスタッシュ構文) {{ こんなの }}
属性バインディング(v-bind)
イベントリスナー(v-on)
双方向バインディング(v-model)
リストレンダリング(v-for)

<style> </style> デザイン担当
コンポーネントの見た目 デザイン(CSS)を記述するところ
ここにデザインがまとまってるので直感的に分かりやすい

・デザインの適用
template内の要素に対し色、サイズ、レイアウトなどの装飾を適用
・HTMLとの連携
で指定した class="title"に対し
.title { color:red } のようにスタイルを当てる

これら3つが連携することで、Vue.jsの最大の特徴である**
「リアクティビティ(データが変わると画面が自動で変わる仕組み)」**が実現

script でデータが更新される。
template がその変化を検知して、表示を自動で書き換える。
style が、その構造に常に正しいデザインを適用し続ける。

②script の中身を整理

ここでは初期値の設定やロジックを担当します。

Image from Gyazo

Vue.js
<script setup> //ロジック(JS)部分はscriptタグで囲む

import { ref } from 'vue' //vueをref型にて定義する

let id = 0 // 初期値に0を入れる


const newTodo = ref('')  // newTodoに ref''を入れる テキストボックス部分
const todos = ref([      // todosの中にref型の配列を入れる
  { id: id++, text: 'Learn HTML' },  // idは1ずつ足していき、それぞれのtextを追加
  { id: id++, text: 'Learn JavaScript' }, // ++で1追加という意味
  { id: id++, text: 'Learn Vue' }
])

// 下記別解説 変数 addTodoを定義 新しい予定を入れるためのテキストボックスの処理をする
function addTodo() {
  todos.value.push({ id: id++, text: newTodo.value }) 
  newTodo.value = ''
}
// 下記別解説変数 removeTodoを定義 Xボタンを押して消す処理関連
function removeTodo(todo) {
  todos.value = todos.value.filter((t) => t !== todo)
}
</script>

function(変数)部分を深掘り

スクリーンショット 2026-07-11 15.40.40.png

別解説① addTodoの中身

Vue.js
function addTodo() {
  todos.value.push({ id: id++, text: newTodo.value }) 
  newTodo.value = ''
}

テキストボックスに入力された文字を、リストの「一番後ろ」に
ガッチャンコするイメージ

【データの流れ】

入力: newTodo.value(テキストボックスの中身)を確認。
作成: 新しいToDoカード { id: id++, text: ... } を作成。
追加: todos.value.push() で、配列の末尾にそのカードを投げ入れる。
リセット: newTodo.value = '' で、入力欄を空っぽにする。

別解説② removeTodoの中身

Vue.js
function removeTodo(todo) {
  todos.value = todos.value.filter((t) => t !== todo)
}

「削除」といっても、実際には元のリストから**「消したいやつ以外」を救い出して、
新しいリストを作り直す**という「ふるい(まさにフィルター)」にかけるイメージ。

【データの流れ】

指定: 消したい項目(todo)を受け取る。
選別: filter メソッドを使い、リストを1つずつチェックする(これが (t))。
判定: 「それは消したいやつですか?(t !== todo)」と聞き、
NO 等しくないと言ったものだけを合格させる。
更新: 合格者だけで作った「新しいリスト」を todos.value に上書きする。

なぜ「NO(等しくない)」と言うものを合格させるのか?

プログラムの t !== todo という条件は、
日本語に訳すと**「t と todo は別物ですか?」**という意味

別物である(YES):消したくない大事なデータだから残す
別物ではない=同じものである(NO):これが消したい対象だから、無視する。

Vueは、「必要なものだけでリストを作り直して、
古いリストと丸ごと入れ替える(todos.value = ...)」という手順を踏むことで、
**「あ、中身が変わった!画面を更新しなきゃ!」**という
リアクティビティのスイッチが入る仕組みになっています。

PHPの array_filter もこれと全く同じ「ふるいにかける」考え方だから
「移し替え」のイメージを持っておくと良い

なぜわざわざ!== の形をとるのか

わざわざ t !== todo じゃなくて
t == todoじゃダメなのか?と思い調べてみたところ、
以下の理由により採用されないことが判明。

1. filter を使う時点で elseに相当する処理がないため

filterメソッドは、配列の要素を一つずつ検査し、
条件が true(合格)になったものだけを新しい箱に入れる仕組み

現在の動き (t !== todo): 「削除したいやつ以外ですか?」
→ 「はい(true)」→ 新しい箱に入れる。
不合格のとき: 「削除したいやつ」だった場合、条件は false になり、
このとき else に相当する処理がないため、その要素はどこにも保存されず、
そのまま消えてなくなってしまう

つまり、filter を使っている時点で、「else(不合格者)は救わない」
というルールが最初から決まってしまっている。

2.if文で同様の処理をするとなると冗長になってしまうため

else を使わずに「削除」と同じ動きを再現する場合、
おおよそ二倍のコードの多さになる。

Vue.js
let 新しいリスト = [];
todos.value.forEach((t) => {
  // 「削除したいやつ以外」なら保存する
  if (t !== todo) {
    新しいリスト.push(t);
  }
  // ここに else は書かない
});

このコードでは、if の条件に合わない(=削除したい対象である)ときは、
単に何の処理も行われずスルーされます。

elseをどうしても採用する例として、
合格者 不合格者それぞれに応じたメッセージを表示したりするなどの場合。
その場合はfilterではなく、 foreachなどと組み合わせる。

scriptまとめ

filter を使った削除処理において else がないのは、
**「不採用通知すら出さない」**仕組みのため

「filter という道具が合格者(true)のことしか見ない性質であり、
消したくないものに true という名札を貼るために !== を使っている」

もしどうしてもelse を使いたいのであれば、それは filter(選別)ではなく、
forEach(全件処理)などの別の道具を使う必要がある。

③template の中身を整理

Image from Gyazo
v-model部分と v-for部分に分けて整理します。

Vue.js
<template>
  <form @submit.prevent="addTodo"> 
    <input v-model="newTodo" required placeholder="new todo">
    <button>Add Todo</button>
  </form>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.text }}
      <button @click="removeTodo(todo)">X</button>
    </li>
  </ul>
</template>

v-model(form)部分

Vue.js
  <form @submit.prevent="addTodo"> 
    <input v-model="newTodo" required placeholder="new todo">
    <button>Add Todo</button>
  </form>

【データの流れ】
1:ユーザーが入力欄に文字を打つ(v-model でデータが溜まる)
2:「Add Todo」ボタンを押す(またはEnterキーを押す)。
.prevent を入れるおかげで画面は真っ白(リロード)にならず、
そのまま addTodo 関数が実行される

3:関数の中で、入力された文字をリストに追加し、入力欄を空にする

スクリーンショット 2026-07-11 23.28.46.png

v-for(ループ処理)部分

Vue.js
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.text }}
      <button @click="removeTodo(todo)">X</button>
    </li>
  </ul>

【データの流れ】
1: v-for="todo in todos" (リストの繰り返し)
これは、JavaScript側の todos 配列の中身を1つずつ取り出して

タグを自動で量産するという命令

PHPでいう foreach ($todos as $todo) と全く同じで、

配列に3つのデータがあれば、
画面にも自動的に3行のリスト<li>の中身が作成される

2 :key="todo.id" (背番号の指定)
Vue.jsが「どの行がどのデータなのか」を正確に識別するための背番号のようなもの
なぜ必要?:
リストの順番が変わったり削除されたりした際、
Vueはこの id を目印にして、画面の更新を最小限の負荷で、かつ正確に行うようにするため

3. {{ todo.text }} (中身の表示)
todo オブジェクトの中にある text(「買い物に行く」などの文字)を
HTMLの中に動的に埋め込む

4. @click="removeTodo(todo)" (削除の引き金)
removeTodo 関数を呼び出す「きっかけ」
@click: ボタンがクリックされた瞬間に、右側の関数を実行
(todo): v-for で今まさに表示している
「その行のToDoデータ」を丸ごと関数に放り投げる

スクリーンショット 2026-07-11 23.30.32.png

削除に関するロジック

1:ユーザーが特定の行(この場合は消したい行)の
「Xボタン」 を押す。(v-on(@)のとこで感知)

2:その行の todo データが removeTodo(todo) 関数に送られる

3:関数の中で filter が発動し、
送られてきた todo 以外のメンバーで新しいリストが作られる

4:todos.value がその新しいリストで上書きされる

5:Vueがデータの変化を検知し、v-for が画面を再描画して、
クリックした行が消える

④まとめ

今回学んだことを図解に書き起こしてみました。
勉強の参考になればと思います。

Gemini_Generated_Image_grl511grl511grl5.png

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?