画面の一部を、データの状態によって出したり隠したりしたい場面は多くあります。ログインの有無、通信の成功や失敗、権限の違いなどです。Vueで最もよく使うのがv-ifです。この記事では、Vue 3(<script setup>)を前提に、基本の使い方から、v-showとの違い、つまずきやすい点までを動くコードで整理します。
基本の使い方
v-ifに渡した式が真のときだけ、その要素が描画されます。偽のときは、要素そのものがDOMに存在しません。ボタンで切り替える例です。
<script setup>
import { ref } from 'vue'
const isVisible = ref(true)
</script>
<template>
<p v-if="isVisible">このテキストは表示されます</p>
<button @click="isVisible = !isVisible">表示/非表示を切り替える</button>
</template>
複数の条件で分岐する
v-else-ifとv-elseを組み合わせると、if〜else ifの文のように書けます。
<script setup>
import { ref } from 'vue'
const status = ref('unknown')
</script>
<template>
<p v-if="status === 'success'">成功しました!</p>
<p v-else-if="status === 'error'">エラーが発生しました。</p>
<p v-else>ステータスが不明です。</p>
<button @click="status = 'success'">成功</button>
<button @click="status = 'error'">エラー</button>
<button @click="status = 'unknown'">不明</button>
</template>
注意点として、v-elseとv-else-ifは、直前の要素がv-ifまたはv-else-ifでなければ認識されません。間に別の要素を挟むと動きません。
<!-- NG:間に別の要素があると v-else が認識されない --> <p v-if="ok">OK</p> <hr> <p v-else>NG</p> <!-- OK:v-if の直後に v-else を置く --> <p v-if="ok">OK</p> <p v-else>NG</p>
templateで複数の要素をまとめて出し分ける
複数の要素を同じ条件で出し分けたいときは、<template>にv-ifを付けます。<template>自体はDOMに出力されないので、余分な要素が増えません。v-elseとv-else-ifも使えます。
<!-- 複数の要素を、余分なDOMを増やさずにまとめて出し分ける -->
<template v-if="isLoggedIn">
<h2>マイページ</h2>
<p>ようこそ、{{ user.name }}さん</p>
<button @click="logout">ログアウト</button>
</template>
<template v-else>
<p>ログインしてください。</p>
</template>
ネストした条件分岐
v-ifの中に、さらにv-ifを置くこともできます。ログイン済みのときだけユーザー名を出し、その中で管理者にだけ追加のメッセージを出す例です。
<script setup>
import { ref } from 'vue'
const user = ref(null)
function login() {
user.value = { name: 'ユーザー名', isAdmin: true }
}
function logout() {
user.value = null
}
</script>
<template>
<div v-if="user">
<p>ようこそ、{{ user.name }}さん!</p>
<p v-if="user.isAdmin">管理者としてログインしています。</p>
</div>
<p v-else>ログインしてください。</p>
<button @click="login">ログイン</button>
<button @click="logout">ログアウト</button>
</template>
v-ifとv-showの違い
表示を切り替える方法には、v-showもあります。違いは、DOMに対する扱いです。
<!-- v-if:条件が偽のとき、要素そのものを描画しない --> <h1 v-if="ok">Hello!</h1> <!-- v-show:常に描画し、CSSの display だけを切り替える --> <h1 v-show="ok">Hello!</h1> <!-- v-show は <template> と v-else には使えない -->
- v-if:偽のとき、要素を描画しません。真になるたびに要素を作り直すため、切り替えのコストは高めです。その代わり、初回の描画で不要な要素を作らないので、初期表示のコストは低くなります。
- v-show:要素は常に描画され、CSSの
displayだけを切り替えます。切り替えのコストは低く、初期の描画コストは高めです。<template>とv-elseには使えません。
目安として、頻繁に切り替えるものはv-show、条件があまり変わらないものはv-ifを使います。
v-ifとv-forを同じ要素に書かない
v-ifとv-forを同じ要素に書くと、優先順位があいまいになります。Vue 3ではv-ifが先に評価されますが、Vue 2ではv-forが先でした。公式も、同じ要素に書くことは避けるよう勧めています。絞り込みはcomputedで行ってから、v-forに渡すと分かりやすくなります。
<script setup>
import { ref, computed } from 'vue'
const todos = ref([
{ id: 1, title: '買い物', done: false },
{ id: 2, title: '掃除', done: true }
])
// 未完了のものだけに絞り込んでから v-for に渡す
const activeTodos = computed(() => todos.value.filter(t => !t.done))
</script>
<template>
<!-- NG:同じ要素に v-if と v-for を書く(Vue 2 と 3 で優先順位が逆) -->
<!-- <li v-for="todo in todos" v-if="!todo.done"> -->
<!-- OK:computed で絞り込む -->
<ul>
<li v-for="todo in activeTodos" :key="todo.id">{{ todo.title }}</li>
</ul>
</template>
リスト表示の詳細はv-forで配列やオブジェクトをループして表示する方法をご覧ください。
関連記事
- 属性のバインディングは属性バインディングの使い方
- ボタンなどのイベントはv-onでDOMイベントを購読する方法
- フォームの入力値は双方向データバインディングでフォームの入力値を反映させる方法
- 通信の状態による出し分けはAPIレスポンスに応じたUI分岐処理の書き方
- 画面サイズによる出し分けはデバイスごとの表示切り替え
よくある質問(FAQ)
displayだけを変えるv-showが向いています。条件があまり変わらない要素は、偽のときに描画しないv-ifが向いています。v-elseやv-else-ifの直前の要素が、v-ifまたはv-else-ifになっていない可能性があります。間に別の要素を挟まないでください。<template v-if="…">で囲みます。<template>はDOMに出力されないので、余分な要素が増えません。v-showは<template>に使えません。v-ifが優先されますが、Vue 2ではv-forが優先で、挙動が逆でした。computedで絞り込んだ配列をv-forに渡す書き方がおすすめです。まとめ
v-ifは、条件が偽のときに要素を描画しない条件分岐です。v-else-ifとv-elseは直前に置き、複数の要素は<template>でまとめます。頻繁に切り替えるならv-show、v-forとの併用はcomputedで絞り込む、という使い分けを押さえておくと迷いません。

