Vue.js: 条件付きとリストレンダリング

最終更新:2026-10-20

条件付きレンダリング(v-if / v-show)とリストレンダリング(v-for)は、Vueテンプレートで最もよく使われる2つのディレクティブです。前者は要素を表示するかどうかを制御し、後者は表示される要素の数を制御します。これらを組み合わせることで、UIのシナリオの90%に対応できます。

v-ifとv-forの5つの主な違いを理解し、:keyの正しい使い方を把握することは、高性能なVueアプリケーションを構築するために不可欠です。

関連ディレクティブ:v-else-if v-show v-for v-if

1. 学習内容

条件付きレンダリング


2. コードの悪夢:ダッシュボードにおける「ネストされたif文」

(1) 課題:5つ入れ子になった v-if タグ――デバッグの悪夢

アリスのダッシュボードのコードが手に負えなくなってしまった:

チャーリーによるコードレビュー:

「アリス、これじゃ読めないよ。v-ifが5段階もネストされてる。もし条件をあと2つ追加したらどうなる?もっとすっきりしたパターンが必要だ。」

(2) Vue での解決策:v-if によるフラット化 + 計算プロパティ

(3) 収益


3. v-if / v-else-if / v-else による条件付きレンダリング

(1) 基本的な構文

(2) template要素

(3) 5つの主なユースケース

シナリオ v-if の使用
ログイン中/未ログイン ✅
読み込み中.../読み込み完了 ✅
データなし/データあり ✅
エラー状態 ✅
表示/非表示を頻繁に切り替える ❌ (v-show を使用)

▶ サンプル:v-if/v-show の5つの活用法

▶ 試してみよう

出力:

TEXT
show = true で v-if と v-show の両方が表示される
show = false で v-if は DOM から消え、v-show は display: none で残る
type = 'B' なら B だけ表示、isAdmin = true で Admin と Edit がまとめて出る

4. v-show と v-if の 5 つの主な違い

(1) 主な相違点

(2) 5つの主な比較点

属性 v-if v-show
DOMの操作 条件がfalseのときは要素を削除 常にDOMに残り、displayプロパティのみが切り替わる
スイッチング性能 低い(その都度破棄・再構築) 高い(CSSのみ)
初期レンダリング 条件がfalseの場合はレンダリングしない 常にレンダリングする(非表示の場合でも)
トランジションとの互換性 ✅ 完全対応(イン/アウトアニメーション) ❌ 未対応
利用シーン たまに切り替える場合 頻繁に切り替える場合

(3) 選定に関する推奨事項

JS
// Switch Occasionally(If you are logged in/Not logged in)→ v-if
<div v-if="isLoggedIn">Welcome, {{ user.name }}</div>
<div v-else>Please login</div>

// Frequent switching (e.g. tab switch) → v-show
<button v-show="isActive">Active</button>
<button v-show="!isActive">Inactive</button>

5. v-for を使ったリストのレンダリング

(1) 配列の反復処理

(2) オブジェクトの反復処理

(3) 数値の反復処理

(4) 文字列の反復処理

▶ サンプル:v-forの5つの活用法

▶ 試してみよう

出力:

TEXT
1 → Apple / Banana / Cherry が li で並ぶ
2 → 1. Apple、2. Banana、3. Cherry
3 → name: Alice、age: 25、email: alice@example.com
4 → 12345、5 → H e l l o(1 文字ずつ span)

▶ サンプル:v-if と v-for の優先順位

VUE
<!-- In Vue 3, v-if has higher priority than v-for (Vue 2 is the opposite) -->
<ul>
 <!-- Vue 3: First determine v-if, then v-for -->
 <li v-for="user in users" v-if="user.isActive" :key="user.id">
 {{ user.name }}
 </li>
 <!-- ✅ But Vue officially recommends using template to separate -->
 
 <!-- ❌ Incorrect wording(Not recommended) -->
 <li v-for="user in users" v-if="shouldShowUsers(user)" :key="user.id">
 {{ user.name }}
 </li>
 
 <!-- ✅ Recommendations:computed Filter -->
 <li v-for="user in activeUsers" :key="user.id">
 {{ user.name }}
 </li>
</template>
▶ 試してみよう

出力:

TEXT
Vue 3 は v-if を先に評価するため、同じ要素に併記すると user 未定義でエラーになり得る
computed の activeUsers を v-for する 3 番目の書き方だけが安全に動く


6. :key の目的とベストプラクティス

(1) :key とは何ですか?

:key VueがDOMを効率的に更新できるよう、各v-for項目に一意の識別子を割り当てます。

(2) なぜ :key が必要なのでしょうか?

100%
graph LR
 A[Original List A B C] --> B[New List B C D]
 B --> C[None key:Possible misalignment]
 B --> D[Has key: Update Correctly]
 
 style C fill:#ff6b6b
 style D fill:#42b883

:key: が存在しない場合

:key が存在する場合:

(3) :key のベストプラクティス

VUE
<!-- ✅ Recommendations:The Only One ID -->
<li v-for="user in users" :key="user.id">

<!-- ✅ Second choice:Unique Field(Email、Mobile phone number, etc.)-->
<li v-for="user in users" :key="user.email">

<!-- ❌ Avoid:index(When the data changes, the alignment is off.)-->
<li v-for="(user, index) in users" :key="index">

<!-- ❌ Never, under any circumstances:random()(Every render is different,Forced Rebuild)-->
<li v-for="user in users" :key="Math.random()">

▶ サンプル::key の 5 つの使い方の比較

▶ 試してみよう

出力:

TEXT
user.id / user.email / 複合キー / index の 4 つはリストが正しく描画される
Math.random() は再描画のたびにキーが変わり、全 li が作り直される

7. リストのフィルタリング/並べ替え/ページネーション

(1) フィルタリングと並べ替え(計算プロパティ)

JS
import { ref, computed } from 'vue'

const items = ref([
 { id: 1, name: 'iPhone', price: 999, category: 'phone' },
 { id: 2, name: 'MacBook', price: 2499, category: 'laptop' },
 { id: 3, name: 'iPad', price: 599, category: 'tablet' }
])

const searchQuery = ref('')
const selectedCategory = ref('all')
const sortBy = ref('price') // 'price' / 'name'

// Filter + Sort(computed Automatic Caching)
const filteredItems = computed(() => {
 let result = items.value
 
 // 1. Filter by search term
 if (searchQuery.value) {
 result = result.filter(item =>
 item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
 )
 }
 
 // 2. Filter by Category
 if (selectedCategory.value !== 'all') {
 result = result.filter(item => item.category === selectedCategory.value)
 }
 
 // 3. Sort
 result = [...result].sort((a, b) => {
 if (sortBy.value === 'price') return a.price - b.price
 if (sortBy.value === 'name') return a.name.localeCompare(b.name)
 return 0
 })
 
 return result
})

(2) ページネーション

JS
const currentPage = ref(1)
const pageSize = ref(10)

// Data after pagination
const paginatedItems = computed(() => {
 const start = (currentPage.value - 1) * pageSize.value
 return filteredItems.value.slice(start, start + pageSize.value)
})

// Total Number of Pages
const totalPages = computed(() =>
 Math.ceil(filteredItems.value.length / pageSize.value)
)

8. 総合演習:商品リストのフィルタ・ソート・ページネーション

▶ サンプル:リストのフィルタリング+ソート+ページネーションの完全な例

▶ 試してみよう

出力:

TEXT
初期表示:iPad - $599、iPhone - $999、MacBook - $2499(価格の昇順)、1 / 1
検索欄に i と入れると iPad と iPhone の 2 件に絞られる
カテゴリで Laptop を選ぶと MacBook - $2499 の 1 件、Previous と Next は無効のまま

(1) 📊 . よくある5つの間違い

エラー 症状 解決策
なし :key リストの位置ずれ、パフォーマンスの低下 :key="item.id" を追加
v-for と v-if の併用 v-if の優先順位の混乱 template で囲むか、computed を使用する
キーとして「index」を使用する ソート時の不整合 一意のIDを使用する
v-show は <transition> をサポートしていません アニメーション化できません 代わりに v-if を使用してください + <transition>
リストが大きすぎる(100,000件以上) ページがフリーズする 仮想スクロールには vue-virtual-scroller を使用する


関連ディレクティブ:v-else-if v-show v-for v-if

❓ よくある質問

Q v-if と v-show のどちらを選べばいいですか?
A たまに切り替える場合は v-if を使い(初期レンダリングの負荷を軽減するため)、頻繁に切り替える場合は v-show を使います(DOM の破壊と再構築を繰り返さないため)。ログイン中/ログアウト中の状態の切り替えには v-if を、タブの切り替えには v-show を使用してください。
Q :key は v-for と併用する必要がありますか?
A パフォーマンスの問題やコンポーネントの状態の不一致を避けるため、:key の使用を強く推奨します。一意の ID (item.id) を最優先とし、次いで index を使用してください。random() は絶対に使用しないでください。
Q v-if と v-for を併用する場合、優先順位はどうなりますか?
A Vue 3 では、v-if が v-for より優先されます(Vue 2 とは逆です)。ただし、公式の推奨としては、これらを併用することは避け、代わりに template を使用して分離するか、computed を使用してフィルタリングすることを推奨しています。
Q template要素の目的は何ですか?
A templateは、DOMにはレンダリングされないVueのロジックラッパー要素です。これは、v-ifやv-forが複数の要素を含む場合に、不要なdiv要素の生成を防ぐために使用されます。
Q リストのパフォーマンスが低い場合はどうすればよいですか?
A 3つの最適化方法があります:(1) :keyを追加する;(2) computedを使用してフィルタリング/ソート結果をキャッシュする; (3) リストが大きい場合(10万件以上)、vue-virtual-scroller を使用して仮想スクロールを行い、表示領域のみを描画するようにする。
Q v-for を使ってオブジェクトを反復処理する際、キーと値にアクセスできますか?
A はい。v-for="(value, key, index) in obj" を使用します。パラメータの順序は、値、キー、インデックスです。
Q データがない場合にプロンプトを表示するにはどうすればよいですか?
A v-if と v-else を組み合わせて使用します:<div v-if="items.length">Column </div><div v-else> Data</div>。
Q リスト内の選択した項目を強調表示するにはどうすればよいですか?
A 各項目に activeIndex を追加してください:@click="activeIndex = item.id" :class="{ active: activeIndex === item.id }"。

📖 まとめ


📝 練習問題

  1. 基礎問題(難易度:⭐)

    v-if/v-else を使用して、ログイン状態とログアウト状態を切り替えます:

    • ログイン中:ユーザー名と「ログアウト」ボタンを表示します
    • ログインしていない場合:「ログインしてください」というメッセージとログインフォームが表示されます
    • テンプレートを使用して、複数の要素をグループ化する
  2. 上級問題(難易度:⭐⭐)

    v-for を使用した To-Do リストの実装:

    • 3つのステータス:「すべて」「進行中」「完了」(3つのボタンを使って切り替える)
    • このリストには、各タスクのステータスとともにタスクが表示されます
    • 各項目には「完了」ボタンと「削除」ボタンがあります
    • 下部に「未完了:N件」と表示されます
  3. チャレンジ問題(難易度:⭐⭐⭐)

    製品のフィルタリング、ソート、ページネーションのシステムを完全に実装する(5ファイル):

    1. 商品データ:30件。商品名、価格、カテゴリ、作成日時が含まれる。
    2. 検索:名前によるあいまい検索を実行する
    3. カテゴリで絞り込み:5つのカテゴリ(すべて/携帯電話/ノートパソコン/タブレット/ヘッドホン)
    4. 並べ替え:価格(昇順/降順)、名前、最新順
    5. ページネーション:1ページあたり10件表示、ページ番号による移動機能付き
    6. パフォーマンス:フィルタリングおよびソート済みの計算結果をキャッシュする
    7. 大規模リストの最適化:1,000件のテスト項目により、スムーズな動作を保証
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%