本文 AI 產出,尚未審核

Vue3 教學 – 模板語法(Template Syntax)

主題:v-show 顯示 / 隱藏


簡介

在 Vue3 的模板語法中,條件渲染是最常見的需求之一。開發者往往需要根據狀態動態地顯示或隱藏某段 UI,這時就會用到兩個指令:v-ifv-show。雖然兩者的目的相同——控制元素的可見性,但底層實作與使用時機卻大不相同。

v-show 透過 CSS 的 display 屬性 來切換元素的顯示狀態,不會把元素從 DOM 中移除。因此在頻繁切換顯示/隱藏的情境下,v-show 的效能往往優於 v-if。本篇文章將深入探討 v-show 的工作原理、使用方式、常見陷阱與最佳實踐,並提供多個實務範例,幫助你在 Vue3 專案中正確且有效地運用這個指令。


核心概念

1. v-show 的基本語法

<div v-show="isVisible">這段文字會根據 isVisible 顯示或隱藏</div>
  • isVisible 必須是 布林值true / false)或能夠被轉換成布林值的表達式。
  • isVisibletrue 時,Vue 會在元素上設定 display: ''(即恢復原本的顯示方式)。
  • isVisiblefalse 時,Vue 會設定 display: none,使元素在畫面上隱藏,但仍保留在 DOM 中。

重點v-show 不會觸發 Vue 的虛擬 DOM 重排,只是單純改變 CSS,故適合頻繁切換的場景。


2. 與 v-if 的差異

特性 v-if v-show
渲染時機 只在條件為 true 時才渲染,條件改變會 掛載 / 卸載 元素 初始渲染時就掛載,之後僅切換 display
DOM 變化 新增 / 移除 DOM 節點 保持 DOM,僅改變樣式
效能 初始渲染較慢,但切換時不會產生重排 初始渲染快,但每次切換都會觸發 CSS 重排
使用情境 條件較少變動、需要大量資源的子組件 需要頻繁顯示/隱藏、子組件渲染成本低

建議:如果元素的顯示狀態在 短時間內頻繁變化,優先考慮 v-show;若條件較為固定且子組件較重,則使用 v-if 以減少不必要的渲染。


3. v-show 的底層實作

當 Vue 解析到 v-show 時,會在編譯階段為目標元素生成一段 runtime directive。這段指令在掛載(mount)階段會檢查綁定值,並在 更新(update)階段根據新值調整 style.display

function toggle(el, value) {
  el.style.display = value ? '' : 'none';
}

因此,只要 value(即綁定的表達式)改變,Vue 只需要一次簡單的 DOM 操作,不會觸發子樹的重新渲染。


4. 多個 v-show 的疊加

在同一元素上使用多個 v-show(例如在父子組件都加上 v-show)時,最終的 display 屬性會受到 最內層指令 的控制。若任一層的條件為 false,元素將最終呈現 display: none。這在 層層控制可見性 時相當有用:

<!-- 父層控制 -->
<div v-show="parentVisible">
  <!-- 子層再做一次控制 -->
  <span v-show="childVisible">子元素</span>
</div>

程式碼範例

以下示範 5 個常見且實用的 v-show 用法,從最基礎到稍微進階的情境都有涵蓋。每段程式碼皆附上說明註解,方便讀者快速理解。

範例 1:最簡單的布林控制

<template>
  <button @click="show = !show">
    {{ show ? '隱藏' : '顯示' }} 文字
  </button>

  <p v-show="show">
    這段文字會根據 show 的值顯示或隱藏。
  </p>
</template>

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

const show = ref(true)   // 初始為 true,文字顯示
</script>

說明showref,點擊按鈕會切換布林值,v-show 立即改變 display


範例 2:結合計算屬性(computed)

<template>
  <input type="text" v-model="keyword" placeholder="輸入關鍵字" />
  <div v-show="hasResult">
    搜尋結果顯示在這裡…
  </div>
</template>

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

const keyword = ref('')

const hasResult = computed(() => {
  // 只要關鍵字長度大於 2,就視為有搜尋結果
  return keyword.value.length > 2
})
</script>

說明:使用 computed 讓條件判斷更具可讀性,且在 keyword 改變時自動重新計算。


範例 3:在表格中切換行的可見性

<template>
  <table class="table-auto border-collapse w-full">
    <thead>
      <tr class="bg-gray-100">
        <th class="p-2">#</th>
        <th class="p-2">名稱</th>
        <th class="p-2">狀態</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="item in items" :key="item.id">
        <td class="p-2">{{ item.id }}</td>
        <td class="p-2">{{ item.name }}</td>
        <td class="p-2">
          <button @click="item.visible = !item.visible">
            {{ item.visible ? '隱藏' : '顯示' }}
          </button>
        </td>
      </tr>
      <!-- 詳細資訊列,依據 item.visible 控制顯示 -->
      <tr v-for="item in items" :key="'detail-' + item.id" v-show="item.visible" class="bg-gray-50">
        <td colspan="3" class="p-2">
          這是 {{ item.name }} 的詳細說明…(可以放任何 HTML)
        </td>
      </tr>
    </tbody>
  </table>
</template>

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

const items = reactive([
  { id: 1, name: 'Apple', visible: false },
  { id: 2, name: 'Banana', visible: false },
  { id: 3, name: 'Cherry', visible: false },
])
</script>

說明:使用 v-show 在同一 tbody 中切換「詳細資訊」列的顯示,避免因 v-if 產生額外的 DOM 重排。


範例 4:結合過渡(transition)效果

v-show<transition> 搭配時,需要使用 appear 或自訂 CSS 來控制過渡。以下示範淡入淡出的動畫:

<template>
  <button @click="open = !open">
    {{ open ? '關閉' : '開啟' }} 抽屜
  </button>

  <transition name="fade">
    <div v-show="open" class="drawer">
      <p>這是一段抽屜內容,使用 fade 轉場。</p>
    </div>
  </transition>
</template>

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

const open = ref(false)
</script>

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
.drawer {
  padding: 1rem;
  background: #f5f5f5;
  border: 1px solid #ddd;
  margin-top: 0.5rem;
}
</style>

說明v-show 本身不會觸發過渡,但配合 <transition> 可以在 display 變化前後加上 CSS 動畫,提供更好的使用者體驗。


範例 5:在組件內部使用 v-show 控制插槽內容

<!-- Modal.vue -->
<template>
  <div class="modal-overlay" v-show="visible" @click.self="close">
    <div class="modal-content">
      <slot></slot>
      <button @click="close" class="mt-2">關閉</button>
    </div>
  </div>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  modelValue: { type: Boolean, default: false }
})
const emit = defineEmits(['update:modelValue'])

const visible = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})

function close() {
  visible.value = false
}
</script>

<style scoped>
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-content {
  background: white;
  padding: 1.5rem;
  border-radius: 0.5rem;
}
</style>
<!-- App.vue -->
<template>
  <button @click="showModal = true">開啟 Modal</button>

  <Modal v-model="showModal">
    <h2 class="text-xl">這是插槽內容</h2>
    <p>使用 v-show 控制整個 Modal 的顯示與隱藏。</p>
  </Modal>
</template>

<script setup>
import { ref } from 'vue'
import Modal from './components/Modal.vue'

const showModal = ref(false)
</script>

說明:在自訂 Modal 組件中,v-show 控制整個遮罩層的可見性,配合 v-modelmodelValue)實現雙向綁定,使外部使用者可以輕鬆開關 Modal。


常見陷阱與最佳實踐

陷阱 可能的問題 解法或最佳實踐
誤用於大型子組件 子組件渲染成本高,頻繁切換 display 仍會保留大量記憶體、事件監聽等。 若子組件較重,改用 v-if 讓 Vue 在不需要時 卸載
與 CSS display 衝突 直接在樣式表中設定 display: blockinline,但 v-show 會覆寫為 none,導致意外行為。 使用 CSS class 來設定顯示樣式,讓 v-show 只負責 none/'' 切換。
過渡效果失效 直接在 v-show 元素上寫 transition,但 Vue 在切換 display 前不觸發過渡。 必須把元素包在 <transition> 中,或自行在 v-show 前後加入動畫類別。
多層 v-show 造成維護困難 父子層都使用 v-show,條件分散,難以追蹤最終可見性。 集中管理:將可見性狀態提升至共同父層,或使用 computed 產生單一布林值。
在 SSR(Server‑Side Rendering)中誤用 SSR 預渲染時,v-show 仍會產生元素,若不希望在首屏渲染出來,會造成 FOUC(Flash of Unstyled Content)。 結合 v-if 或在 mounted 後再切換 v-show,避免首屏顯示不必要的內容。

最佳實踐小結

  1. 頻繁切換 → 使用 v-show
  2. 初始渲染成本高 → 使用 v-if
  3. 需要過渡動畫 → 搭配 <transition>,或自行控制 CSS。
  4. 避免 CSS 衝突 → 只在樣式表中設定 display預設,讓 v-show 處理 none
  5. 狀態集中 → 把可見性布林值提升至最近的共同父層,避免多層 v-show 互相干擾。

實際應用場景

場景 為何選擇 v-show
Tab 切換 切換 Tab 時只改變內容區塊的可見性,保持 DOM 結構,避免重新渲染子組件。
下拉選單 / 手風琴 使用者點擊展開/收合時頻繁觸發,v-show 能即時切換 display,提供流暢體驗。
載入中遮罩 只需要顯示或隱藏遮罩層,不需要重新渲染遮罩內部的結構。
表格行的詳細資訊 如前面的範例,點擊展開行的細節,保持表格的排版不變。
Modal / Dialog 透過 v-show 控制整個 Modal 的顯示,配合過渡可得到平滑的開關效果。
權限控制的 UI 某些功能只對特定使用者顯示,切換權限時即時隱藏/顯示相關按鈕,避免重新渲染整個頁面。

總結

v-show 是 Vue3 中 輕量級的條件渲染指令,它透過改變元素的 display 屬性來達成顯示與隱藏,不會移除 DOM。這使得它在需要頻繁切換可見性的 UI(如 Tab、手風琴、Modal)上表現尤為出色。

在使用時,務必留意以下要點:

  • 頻繁切換 → v-show渲染成本高 → v-if
  • 若要加上過渡效果,請配合 <transition>
  • 避免與直接設定的 CSS display 發生衝突,建議使用 class 方式統一管理樣式。
  • 在大型子組件或 SSR 場景中,評估是否真的需要保留在 DOM,必要時改用 v-if

掌握了 v-show 的原理與最佳實踐,你就能在 Vue3 專案中靈活地控制 UI 可見性,提升使用者體驗與程式效能。祝你開發順利,寫出更佳的 Vue 應用!