はじめに
この記事では、Vue の v-show と v-if の違いを整理します。
どちらも「条件によって表示を切り替える」ための記法ですが、内部の動きは同じではありません。
見た目が似ているため、最初は「どちらでも同じでは」と感じやすいです。
ただし実際には、次の差があります。
-
v-ifは要素や子コンポーネントを描画するかどうかを切り替える -
v-showは要素を残したまま、表示だけを切り替える - そのため、向いている場面も少し違う
結論だけ先に言うと、頻繁に表示切り替えするなら v-show、そもそも出ないことが多いなら v-if から考えると整理しやすいです。
まず結論
違いを最短で整理すると次のとおりです。
| 項目 | v-if |
v-show |
|---|---|---|
| 非表示時 | DOM に出さない | DOM に残したまま display: none 相当で隠す |
| 初回表示 | 条件が false なら描画しない |
最初から描画する |
| 切り替え時 | 作成・破棄が発生する | 表示だけ切り替える |
| 向いている場面 | あまり表示が切り替わらない要素 | 頻繁に表示を切り替える要素 |
template で使えるか |
使える | 使えない |
この表だけ覚えておけば、多くの場面では困りません。
v-if は条件に合うときだけ描画する
v-if は、条件に合うときだけその要素を描画します。
条件に合わなければ、その要素は最初から描画されません。
<script setup lang="ts">
import { ref } from "vue"
const isAdmin = ref(false)
</script>
<template>
<p v-if="isAdmin">
管理者メニュー
</p>
</template>
この例では、isAdmin が false の間は p 要素自体が描画されません。
この動きのポイントは次のとおりです。
- 初回表示で条件が
falseなら、そのブロックはまだ作られない - 条件が
trueになった時点で初めて描画される - 再び
falseになると、そのブロックは破棄される
Vue 公式でも、v-if は実際の conditional rendering であり、切り替え時にイベントリスナーや子コンポーネントが破棄・再作成される、と説明されています。
v-show は描画した上で見えなくする
v-show は、要素を描画したまま表示だけ切り替えます。
<script setup lang="ts">
import { ref } from "vue"
const isOpen = ref(false)
</script>
<template>
<div v-show="isOpen">
メニューの中身
</div>
</template>
この場合、isOpen が false でも div は描画されています。
ただし、表示だけが消えています。
実際には display: none 相当の形で非表示になります。
そのため、v-show のポイントは次のとおりです。
- 最初から要素は作られる
- 表示、非表示の切り替えは軽い
- 要素自体は残る
ただし、初回表示では要素が最初から生成されるため、描画コストは v-if より高くなることがあります。
「今は見せないが、すぐまた出すかもしれない」ものにはこちらが向いています。
同じ見た目でも内部の意味は違う
画面だけ見ると、v-if でも v-show でも「消えている」ように見えます。
ただし、Vue にとっては意味がかなり違います。
たとえば、モーダル、開閉パネル、タブの中身のように何度も開いたり閉じたりする UI を考えます。
-
v-ifは開くたびに作り、閉じるたびに消す -
v-showは最初に作っておき、見せるか隠すかだけ切り替える
この差があるため、「頻繁に切り替えるなら v-show」という判断が出てきます。
逆に、管理者だけに見せる領域や、ある条件でしか一度も出ない重いブロックなら、v-if のほうが自然です。
どう使い分けるか
迷ったときは、次の基準で考えると整理しやすいです。
頻繁に開閉するなら v-show
例えば次のような UI です。
- 開閉メニュー
- アコーディオン
- タブの切り替え
- フィルター欄の表示、非表示
この種の UI は、表示の切り替えが多いです。
そのため、毎回作って消すより、最初から置いておいて見せ方だけ変える v-show が合いやすいです。
そもそも出ないことが多いなら v-if
例えば次のような UI です。
- 権限があるユーザーにだけ見せる管理メニュー
- エラー時だけ出す警告ブロック
- データがあるときだけ出す詳細セクション
この種の UI は、初回表示で出ないことも多く、何度も切り替わらないこともあります。
その場合は v-if のほうが意図に合いやすいです。
template タグで使えるのは v-if
ここは地味ですが、実務ではよく効きます。
v-if は <template> に付けられます。
複数要素をまとめて条件分岐したいときに便利です。
<template v-if="showDetail">
<h2>詳細</h2>
<p>説明文です。</p>
<button>編集</button>
</template>
一方で v-show は <template> には使えません。
v-show は実際に描画される要素の display を切り替える仕組みだからです。
template 自体は最終的に DOM に残らないため、v-show を適用する対象になりません。
複数要素をまとめて表示、非表示したい場合は、次のどちらかで考えることになります。
- ラッパー要素に
v-showを付ける - 本当に要素ごと出し分けたいなら
v-ifを使う
よくある誤解
v-show のほうが常に高性能というわけではない
v-show は切り替えコストが低いので便利です。
ただし、最初から描画はされます。
そのため、初回表示で不要な要素まで大量に持つ場合は、単純に v-show を増やせばよいわけではありません。
v-show は要素を DOM に残すため、初回レンダリング時の負荷が問題になるケースもあります。
見た目が同じでもライフサイクルは同じではない
v-if は子コンポーネントごと消えるため、内部 state やイベントリスナーの扱いにも影響します。
見た目だけで判断すると、あとで意図しない動きに見えることがあります。
迷ったらまずは意図で選ぶ
性能だけで決めるより、まず次の問いで選ぶほうが失敗しにくいです。
- この要素は存在自体を切り替えたいのか
- それとも存在はさせたまま見え方だけ変えたいのか
この問いに答えると、かなりの確率で v-if と v-show は自然に決まります。
まとめ
v-if と v-show は、どちらも条件付き表示のための記法です。
ただし、役割は同じではありません。
ポイントをまとめると次のとおりです。
-
v-ifは要素を描画するかどうかを切り替える -
v-showは要素を残したまま表示だけ切り替える - 頻繁に切り替えるなら
v-showが合いやすい - そもそも出ないことが多いなら
v-ifが合いやすい - 複数要素をまとめて切り替えたいときは
template + v-ifが使いやすい
Vue の条件分岐で迷ったら、まず「消したいのは表示だけか、要素そのものか」を考えると整理しやすくなります。
Vue のディレクティブ全体を俯瞰したい場合は Vueの主要ディレクティブをReactで書くとどうなるか でも整理しています。
テンプレート記法そのものの考え方については VueのテンプレートとReactのJSXは何が違うのか も関連します。