Vue3でref、reactive、computed、watch、v-bind、v-if、v-show、v-forを学んできましたが、言葉だけを追うと役割が重なって見えました。
今回はゲームの持ち物検索画面を例に、<script setup>で作ったデータが、<template>のどこへ反映されるかを中心に整理してみます。
持ち物を検索して一覧を絞り込むだけの小さな画面にすると、元データ・検索語・表示用データ・画面表示のつながりを追いやすそうです。
まだ途中段階なので、勘違いなどあれば教えていただけるとうれしいです。
今日やったこと
Vue3の要素を「何の機能か」ではなく、「どこで値を持ち、どこへ反映するか」で見直しました。
Vueのテンプレートは、コンポーネント内のデータとブラウザに描画されるDOMを結び付けるHTMLベースの構文です。リアクティブな値をテンプレートで参照すると、値の変更に合わせて必要な表示が更新されます。Vue.js公式ドキュメント vueframework
![]() |
「script側で持ち物や検索語を覚えて、template側で一覧画面として見せる。まずはこの往復を追ってみよう。」 |
Vueの.vueファイルは、今のところ次の3つへ分けて考えています。
-
<script setup>:状態、計算結果、関数、監視処理を書く -
<template>:<script setup>の値を読み、画面へ出したり、操作で値を変えたりする -
<style>:画面上の色・余白・配置などを整える
つまずいたところ
<script setup>の値と<template>で使う値を、同じ場所のデータのように考えないことが大事そうです。
<template>は検索欄や一覧を置く場所ですが、検索語や持ち物データを保管する場所ではありません。検索欄へ入力した文字は、<template>の@inputを通って<script setup>のkeywordへ入り、そこから表示用のcomputedへ渡ります。
この図の流れは、次のように読んでいます。
- プレイヤーが
<template>の検索欄へ文字を入力する -
@inputが<script setup>のkeywordを更新する -
computedがitemsとkeywordからfilteredItemsを作る -
<template>のv-forがfilteredItemsを一覧として表示する -
filteredItemsが0件なら、v-ifが案内文を表示する
ゲームでいうと、itemsは持ち物倉庫、keywordはプレイヤーが入力した検索条件、filteredItemsは現在メニューに並べるアイテム一覧です。v-forは、その一覧を画面上の行やカードとして配置する役割になります。
![]() |
「templateは倉庫そのものではなくて、script側の倉庫を見ながら、いま必要なアイテムだけをメニューに並べる役なんだね。」 |
今の理解(メモ)
データの出どころと反映先を分けると、Vue3の要素を次のように整理できそうです。
| 場所 | 要素 | データの出どころ | 反映先・役割 | 持ち物画面での例 |
|---|---|---|---|---|
<script setup> |
ref |
初期値、templateからの入力・クリック |
computed、template |
keyword、選択中ID、所持金 |
<script setup> |
reactive |
初期値、templateからの操作 |
computed、v-showなど |
検索設定、メニュー設定 |
<script setup> |
computed |
ref、reactive、元データ |
v-for、v-if、{{ }}
|
filteredItems、表示件数 |
<script setup> |
watch |
指定したリアクティブな値 | 保存、取得、ログなど | 検索語変更後の処理 |
<script setup> |
watchEffect |
処理内で参照したリアクティブな値 | 同期処理、ログなど | 複数状態を使う処理 |
<template> |
{{ }} |
script側の値 | 文字として画面表示 | アイテム名、検索語、件数 |
<template> |
v-bind / :
|
script側の値 | HTML属性へ反映 |
:class、:disabled、:src
|
<template> |
@input / @click
|
プレイヤー操作 | script側の値や関数へ渡す | 検索語入力、ヘルプ開閉 |
<template> |
v-if |
真偽値、件数 | 必要なときだけ要素を表示 | 0件の案内 |
<template> |
v-show |
真偽値 | 要素を残して表示を切替 | ヘルプの開閉 |
<template> |
v-for |
配列・オブジェクト | データ数に応じて要素を並べる | 持ち物一覧 |
refは単体のリアクティブな値を作るために使い、<script setup>では.valueで中身を扱います。テンプレートでは通常.valueを書かず、そのまま値を利用できます。Vue.js公式ドキュメント vueframework
computedはリアクティブな依存先から派生した値を作るための仕組みで、依存先が変化するまで結果を再利用します。検索後の一覧や合計値のような「画面に出すための値」に向いていそうです。Vue.js公式ドキュメント vueframework
小さな画面の例
持ち物を検索して一覧を絞り込む例です。コメントで、データが<script setup>から<template>のどこへ届くかを明示します。
<script setup>
import { computed, ref, watch } from 'vue'
// 【script:元データ】
// 持ち物倉庫にあるアイテム。
// templateのv-forへ直接渡さず、computedで表示用の配列を作る。
const items = ref([
{ id: 1, name: '木の剣' },
{ id: 2, name: '回復薬' },
{ id: 3, name: '鉄の盾' },
])
// 【script:画面操作で変わる状態】
// templateの検索欄に入力された文字を保存する。
const keyword = ref('')
// 【script:画面に渡す表示用データ】
// itemsとkeywordを使い、templateのv-forとv-ifへ渡す配列を作る。
// itemsまたはkeywordが変わると、Vueがこの結果を更新する。
const filteredItems = computed(() => {
return items.value.filter((item) => {
return item.name.includes(keyword.value)
})
})
// 【script:値の変化後に行う処理】
// keywordが変わったあとに実行される。
// 現時点では確認用のログだけで、外部検索などは追加していない。
watch(keyword, (newKeyword) => {
console.log('検索語が変わりました:', newKeyword)
})
</script>
<template>
<section class="inventory">
<!--
【script → template】
:valueでscript側のkeywordを入力欄に表示する。
【template → script】
@inputで入力内容をscript側のkeywordへ戻す。
-->
<input
:value="keyword"
placeholder="アイテム名を検索"
@input="keyword = $event.target.value"
>
<!--
【script → template】
filteredItemsが0件のときだけ、この案内を表示する。
-->
<p v-if="filteredItems.length === 0">
条件に合うアイテムはありません。
</p>
<!--
【script → template】
computedで作ったfilteredItemsをv-forで並べる。
item.idは、各アイテムをVueが見分けるためのkey。
-->
<ul v-else>
<li v-for="item in filteredItems" :key="item.id">
{{ item.name }}
</li>
</ul>
</section>
</template>
このコードで、itemsは元データ、keywordは検索欄から入る状態、filteredItemsは画面へ渡すために計算したデータです。
操作ごとの流れ
検索欄へ「剣」と入力する
プレイヤーが<template>の検索欄へ「剣」と入力すると、keywordが更新されます。keywordを参照しているfilteredItemsが再計算され、v-forが「木の剣」だけを一覧へ表示します。
検索欄を空に戻す
検索欄を空にすると、keywordは空文字列になります。includes('')はすべてのアイテム名に一致するため、この例ではfilteredItemsへ全アイテムが入り、v-forが3件を表示します。
該当する持ち物がない場合
たとえば「弓」と入力し、該当するアイテムがない場合は、filteredItemsが空配列になります。v-if="filteredItems.length === 0"が真になり、一覧の代わりに案内文を表示します。
watchとwatchEffectの位置
この検索画面では、computedが「画面へ出す持ち物一覧」を作っています。一方でwatchとwatchEffectは、画面表示そのものではなく、値が変化したあとに処理したいことがある場合に使うものとして整理しています。
-
computed:itemsとkeywordから、画面に表示するfilteredItemsを作る -
watch:keywordのように、監視する対象を指定して処理する -
watchEffect:処理の中で使ったリアクティブな値を自動追跡する -
watch・watchEffect:保存、ログ、データ取得など、画面に値を出す以外の処理を置く
watchは監視対象を明示でき、watchEffectはコールバック内で同期的に読んだリアクティブな依存先を自動で追跡します。Vue.js公式ドキュメント vueframework
![]() |
「画面の一覧を作るならcomputed。検索語が変わった記録を残すならwatch。どこへ結果を渡したいかで分けると選びやすそう。」 |
v-showを使う場所
v-showは、<script setup>の真偽値を使い、要素を残したまま表示・非表示を切り替えます。検索のヘルプを頻繁に開閉する場面なら、次のように使えそうです。
<script setup>
import { ref } from 'vue'
// script側で、ヘルプを開いているかを持つ
const isHelpOpen = ref(false)
</script>
<template>
<!-- template側の操作でscript側の状態を変える -->
<button @click="isHelpOpen = !isHelpOpen">
ヘルプを開閉
</button>
<!-- script側のisHelpOpenを受け取り、見た目だけ切り替える -->
<p v-show="isHelpOpen">
アイテム名を入力すると、持ち物一覧を絞り込めます。
</p>
</template>
v-ifは条件に応じて要素を描画し、v-showは要素をDOMに残したままCSSのdisplayを切り替えます。0件の案内にはv-if、頻繁に開閉するヘルプにはv-showを使う形が分かりやすそうです。Vue.js公式ドキュメント vueframework
次にやりたいこと
今の検索画面を少しずつ広げながら、データの流れを確かめたいです。
-
v-modelを使い、:valueと@inputをまとめた場合のデータの往復を確認する -
selectedItemIdをrefで持ち、クリックしたアイテムの見た目を:classで変える - アイテムに攻撃力を追加し、
computedで合計値を作って{{ totalAttack }}へ反映する -
watchで検索語の変更を受け、検索履歴を保存する形を試す -
watchEffectで複数の状態を使う処理を書き、watchとの使い分けを確認する -
v-forで並べたアイテムをItemCard.vueへ渡し、親の配列データが子コンポーネントの画面へ届く流れを試す
今日の整理
Vue3では、データが次の順番で動くものとして整理しています。
| 操作・きっかけ |
<script setup>で変わる値 |
計算・監視 |
<template>で変わる場所 |
|---|---|---|---|
| 検索欄へ文字を入力 | keyword |
computed(filteredItems)、watch(keyword)
|
v-forの持ち物一覧 |
| 検索語が空になる | keyword |
computed(filteredItems) |
全アイテムの一覧 |
| 該当データが0件になる | filteredItems |
filteredItems.length |
v-ifの案内文 |
| ヘルプを開閉する | isHelpOpen |
なし |
v-showのヘルプ |
| アイテムを選択する | selectedItemId |
必要に応じてcomputed
|
:classによる選択中の見た目 |
| 所持金が変わる | gold |
computed(canBuy) |
{{ gold }}、:disabledの購入ボタン |
おわりに
Vue3の要素を機能名だけで覚えるより、<script setup>で作ったデータが<template>のどこへ届くのかを追うと、画面の変化を読みやすくなりました。
今回の持ち物検索画面では、itemsが元データ、keywordがプレイヤー操作で変わる状態、filteredItemsが画面に渡す表示用データ、v-forが画面へ並べる役として分けられました。次はpropsを使い、親コンポーネントのitemsが子コンポーネントのアイテムカードへ渡る流れも確認してみたいです。
学習中の内容なので、勘違いや理解不足もあると思います。もし気になる点があれば教えてもらえるとうれしいです。
注意事項
本ブログに掲載している内容は、私個人の見解であり、
所属する組織の立場や戦略、意見を代表するものではありません。
あくまでエンジニアとしての経験や考えを発信していますのでご了承ください。

