Vue 模板語法

Vue 使用基於 HTML 的模板語法,讓你可以聲明式地將元件實例的資料綁定到渲染的 DOM 上。所有 Vue 模板都是合法的 HTML,可以被符合規範的瀏覽器和 HTML 解析器解析。

文字插值(Text Interpolation)

最基本的資料綁定形式是使用雙大括號(Mustache 語法)進行文字插值:

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

const message = ref('Hello, Vue!')
</script>

<template>
  <p>訊息:{{ message }}</p>
</template>

{{ message }} 會被替換為 message 的值,並且當 message 改變時,畫面也會自動更新。

在插值中使用 JavaScript 表達式

雙大括號中可以使用任何有效的 JavaScript 表達式:

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

const number = ref(10)
const ok = ref(true)
const message = ref('hello')
const id = ref('item-1')
</script>

<template>
  <!-- 數學運算 -->
  <p>{{ number + 1 }}</p>

  <!-- 三元運算子 -->
  <p>{{ ok ? 'YES' : 'NO' }}</p>

  <!-- 呼叫方法 -->
  <p>{{ message.split('').reverse().join('') }}</p>

  <!-- 模板字串 -->
  <p>{{ `ID: ${id}` }}</p>
</template>
只能使用表達式(expressions),不能使用陳述式(statements)。例如 {{ if (ok) { return message } }} 是無效的,因為 if 是陳述式。

插值的限制

每個綁定只能包含單一表達式,以下是不合法的:

<!-- 這是陳述式,不是表達式 -->
{{ var a = 1 }}

<!-- 流程控制也不行,請使用三元運算子 -->
{{ if (ok) { return message } }}

原始 HTML(Raw HTML)

雙大括號會將資料解析為純文字,如果你想輸出真正的 HTML,需要使用 v-html 指令:

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

const rawHtml = ref('<span style="color: red">這是紅色文字</span>')
</script>

<template>
  <!-- 顯示為純文字 -->
  <p>使用雙括號:{{ rawHtml }}</p>

  <!-- 解析為 HTML -->
  <p>使用 v-html:<span v-html="rawHtml"></span></p>
</template>

輸出結果:

  • 使用雙括號:<span style="color: red">這是紅色文字</span>
  • 使用 v-html:這是紅色文字
在網站上動態渲染任意 HTML 是非常危險的,因為很容易導致 XSS 攻擊。只對可信任的內容使用 v-html絕對不要將使用者輸入的內容用 v-html 渲染。

v-text 指令

v-text 用於更新元素的文字內容,效果等同於雙大括號,但會覆蓋元素內的所有內容:

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

const message = ref('Hello')
</script>

<template>
  <!-- 以下兩種寫法效果相同 -->
  <span v-text="message"></span>
  <span>{{ message }}</span>
</template>

大多數情況下,使用 {{ }} 會更直覺,v-text 較少使用。

屬性綁定(Attribute Bindings)

雙大括號不能用在 HTML 屬性中,要綁定屬性需要使用 v-bind 指令:

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

const dynamicId = ref('my-id')
const imageUrl = ref('https://vuejs.org/images/logo.png')
</script>

<template>
  <div v-bind:id="dynamicId">這個 div 的 id 是動態的</div>
  <img v-bind:src="imageUrl" alt="Vue Logo">
</template>

簡寫語法

因為 v-bind 非常常用,Vue 提供了簡寫語法,直接使用 : 即可:

<template>
  <!-- 完整語法 -->
  <div v-bind:id="dynamicId"></div>

  <!-- 簡寫語法(推薦) -->
  <div :id="dynamicId"></div>
  <img :src="imageUrl" alt="Vue Logo">
</template>

同名簡寫(Vue 3.4+)

從 Vue 3.4 開始,如果屬性名稱和綁定的變數名稱相同,可以進一步簡化:

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

const id = ref('my-id')
</script>

<template>
  <!-- 完整寫法 -->
  <div :id="id"></div>

  <!-- 同名簡寫(Vue 3.4+) -->
  <div :id></div>
</template>

布林屬性(Boolean Attributes)

對於布林屬性(如 disabledreadonly),Vue 會根據值的真假來決定是否渲染該屬性:

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

const isButtonDisabled = ref(true)
</script>

<template>
  <button :disabled="isButtonDisabled">按鈕</button>
</template>
  • isButtonDisabledtrue 或空字串 "":渲染為 <button disabled>
  • isButtonDisabledfalsenullundefineddisabled 屬性不會被渲染

動態綁定多個屬性

如果你有一個物件包含多個屬性,可以使用不帶參數的 v-bind 一次綁定所有屬性:

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

const inputAttrs = reactive({
  id: 'username',
  type: 'text',
  placeholder: '請輸入使用者名稱'
})
</script>

<template>
  <!-- 一次綁定多個屬性 -->
  <input v-bind="inputAttrs">

  <!-- 等同於 -->
  <input :id="inputAttrs.id" :type="inputAttrs.type" :placeholder="inputAttrs.placeholder">
</template>

動態參數(Dynamic Arguments)

你也可以在指令的參數中使用 JavaScript 表達式,用方括號包起來:

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

const attributeName = ref('href')
const url = ref('https://vuejs.org')

const eventName = ref('click')
const handleEvent = () => {
  console.log('事件觸發了!')
}
</script>

<template>
  <!-- 動態屬性名稱 -->
  <a :[attributeName]="url">Vue 官網</a>

  <!-- 動態事件名稱 -->
  <button @[eventName]="handleEvent">點我</button>
</template>

動態參數的限制

  1. 值的限制:動態參數應該是字串,或者是 nullnull 會移除該綁定)
  2. 語法限制:避免使用空格和引號等字元,因為 HTML 屬性名稱不允許這些字元
<!-- 這會觸發編譯警告 -->
<a :['foo' + bar]="value"> ... </a>

如果需要複雜的動態參數,建議使用 computed 屬性。

Class 與 Style 綁定

Vue 對 classstyle 的綁定做了特別加強,表達式的值除了字串外,還可以是物件或陣列。

綁定 Class - 物件語法

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

const isActive = ref(true)
const hasError = ref(false)
</script>

<template>
  <!-- 物件語法 -->
  <div :class="{ active: isActive, 'text-danger': hasError }">
    Class 綁定範例
  </div>
</template>

<style>
.active { font-weight: bold; }
.text-danger { color: red; }
</style>

渲染結果:<div class="active">Class 綁定範例</div>

綁定 Class - 陣列語法

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

const activeClass = ref('active')
const errorClass = ref('text-danger')
</script>

<template>
  <!-- 陣列語法 -->
  <div :class="[activeClass, errorClass]">
    Class 綁定範例
  </div>
</template>

渲染結果:<div class="active text-danger">Class 綁定範例</div>

綁定 Style - 物件語法

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

const activeColor = ref('red')
const fontSize = ref(30)
</script>

<template>
  <!-- 物件語法 - CSS 屬性名稱使用 camelCase 或 kebab-case -->
  <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">
    Style 綁定範例
  </div>

  <!-- 也可以用 kebab-case,但需要加引號 -->
  <div :style="{ color: activeColor, 'font-size': fontSize + 'px' }">
    Style 綁定範例
  </div>
</template>

綁定 Style - 陣列語法

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

const baseStyles = reactive({
  color: 'blue',
  fontSize: '16px'
})

const overridingStyles = reactive({
  fontWeight: 'bold'
})
</script>

<template>
  <!-- 合併多個樣式物件 -->
  <div :style="[baseStyles, overridingStyles]">
    Style 綁定範例
  </div>
</template>

指令(Directives)

指令是帶有 v- 前綴的特殊屬性。指令的職責是當表達式的值改變時,將相應的效果套用到 DOM 上。

<p v-if="seen">現在你看到我了</p>

這裡 v-if 指令會根據 seen 的真假值來插入或移除 <p> 元素。

指令語法結構

v-指令名稱:參數.修飾符="值"

v-on:submit.prevent="onSubmit" 為例:

  • 指令名稱on
  • 參數submit(監聽的事件名稱)
  • 修飾符prevent(呼叫 event.preventDefault()
  • onSubmit(要執行的方法)

常見指令一覽

指令說明簡寫
v-bind動態綁定屬性:
v-on監聯事件@
v-model雙向資料綁定
v-if條件渲染
v-show條件顯示/隱藏
v-for列表渲染
v-slot插槽#

更多指令的詳細用法請參考 Vue 指令