Vue 指令(Directives)
指令(Directives)是 Vue 模板中帶有 v- 前綴的特殊屬性。指令的作用是當表達式的值改變時,響應式地將效果套用到 DOM 上。
v-bind - 屬性綁定
v-bind 用於動態綁定一個或多個 HTML 屬性到表達式。
<script setup>
import { ref } from 'vue'
const url = ref('https://vuejs.org')
const isDisabled = ref(true)
</script>
<template>
<!-- 完整語法 -->
<a v-bind:href="url">Vue 官網</a>
<!-- 簡寫語法(推薦) -->
<a :href="url">Vue 官網</a>
<!-- 布林屬性 -->
<button :disabled="isDisabled">按鈕</button>
</template>
綁定多個屬性
<script setup>
import { reactive } from 'vue'
const attrs = reactive({
id: 'my-input',
type: 'text',
placeholder: '請輸入內容'
})
</script>
<template>
<input v-bind="attrs">
</template>
v-on - 事件監聽
v-on 用於監聽 DOM 事件。
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
function greet(name) {
alert(`Hello, ${name}!`)
}
</script>
<template>
<!-- 完整語法 -->
<button v-on:click="increment">點擊次數:{{ count }}</button>
<!-- 簡寫語法(推薦) -->
<button @click="increment">點擊次數:{{ count }}</button>
<!-- 內聯表達式 -->
<button @click="count++">+1</button>
<!-- 帶參數的方法 -->
<button @click="greet('Vue')">打招呼</button>
<!-- 存取原生事件物件 -->
<button @click="(event) => console.log(event.target)">Log Event</button>
<!-- 使用特殊變數 $event -->
<button @click="greet($event.target.textContent)">點我</button>
</template>
事件修飾符
Vue 提供了多種事件修飾符來處理常見的 DOM 事件細節:
<template>
<!-- 阻止預設行為 -->
<form @submit.prevent="onSubmit">
<button type="submit">送出</button>
</form>
<!-- 阻止事件冒泡 -->
<div @click="onClick">
<button @click.stop="onButtonClick">按鈕</button>
</div>
<!-- 事件只觸發一次 -->
<button @click.once="doOnce">只能點一次</button>
<!-- 事件捕獲模式 -->
<div @click.capture="onCapture">...</div>
<!-- 只有事件是從元素本身觸發時才執行 -->
<div @click.self="onSelf">...</div>
<!-- 修飾符可以串接 -->
<a @click.stop.prevent="onClick">連結</a>
</template>
| 修飾符 | 說明 |
|---|---|
.stop | 呼叫 event.stopPropagation() |
.prevent | 呼叫 event.preventDefault() |
.capture | 使用事件捕獲模式 |
.self | 只有事件從元素本身觸發時才執行 |
.once | 事件只會觸發一次 |
.passive | 不會呼叫 preventDefault(),提升滾動效能 |
按鍵修飾符
監聽鍵盤事件時可以使用按鍵修飾符:
<template>
<!-- 只在按下 Enter 時觸發 -->
<input @keyup.enter="submit">
<!-- 按下 Esc 時觸發 -->
<input @keyup.esc="cancel">
<!-- 常用按鍵修飾符 -->
<input @keyup.tab="onTab">
<input @keyup.delete="onDelete">
<input @keyup.space="onSpace">
<input @keyup.up="onArrowUp">
<input @keyup.down="onArrowDown">
<input @keyup.left="onArrowLeft">
<input @keyup.right="onArrowRight">
</template>
系統修飾鍵
<template>
<!-- Ctrl + Click -->
<div @click.ctrl="onCtrlClick">Ctrl + Click</div>
<!-- Alt + Enter -->
<input @keyup.alt.enter="onAltEnter">
<!-- Ctrl + A -->
<input @keyup.ctrl.a="selectAll">
<!-- 系統修飾鍵:ctrl, alt, shift, meta (Mac 的 Command 鍵) -->
</template>
滑鼠按鍵修飾符
<template>
<button @click.left="onLeftClick">左鍵點擊</button>
<button @click.right="onRightClick">右鍵點擊</button>
<button @click.middle="onMiddleClick">中鍵點擊</button>
</template>
v-model - 雙向資料綁定
v-model 在表單元素上建立雙向資料綁定:
<script setup>
import { ref } from 'vue'
const text = ref('')
const checked = ref(false)
const selected = ref('')
</script>
<template>
<!-- 文字輸入 -->
<input v-model="text" type="text">
<p>輸入的文字:{{ text }}</p>
<!-- 核取方塊 -->
<input v-model="checked" type="checkbox">
<p>勾選狀態:{{ checked }}</p>
<!-- 選單 -->
<select v-model="selected">
<option value="">請選擇</option>
<option value="a">選項 A</option>
<option value="b">選項 B</option>
</select>
<p>選擇的值:{{ selected }}</p>
</template>
v-model 修飾符
<script setup>
import { ref } from 'vue'
const msg = ref('')
const age = ref(0)
</script>
<template>
<!-- .lazy:在 change 事件後才更新(而非 input 事件) -->
<input v-model.lazy="msg">
<!-- .number:自動將輸入轉為數字 -->
<input v-model.number="age" type="number">
<!-- .trim:自動去除首尾空白字元 -->
<input v-model.trim="msg">
</template>
更多表單相關用法請參考 表單處理。
v-if / v-else-if / v-else - 條件渲染
根據條件決定是否渲染元素:
<script setup>
import { ref } from 'vue'
const score = ref(85)
const isLoggedIn = ref(false)
</script>
<template>
<!-- 基本用法 -->
<p v-if="isLoggedIn">歡迎回來!</p>
<p v-else>請先登入</p>
<!-- 多重條件 -->
<div v-if="score >= 90">優秀</div>
<div v-else-if="score >= 60">及格</div>
<div v-else>不及格</div>
</template>
在 template 上使用 v-if
如果要同時切換多個元素,可以在 <template> 上使用 v-if:
<template>
<template v-if="isLoggedIn">
<h1>歡迎回來</h1>
<p>您的帳號:{{ username }}</p>
<button @click="logout">登出</button>
</template>
<template v-else>
<h1>請登入</h1>
<button @click="login">登入</button>
</template>
</template>
<template> 只是一個不可見的包裝元素,不會被渲染到 DOM 中。
v-show - 條件顯示
v-show 也是根據條件顯示元素,但它是透過 CSS 的 display 屬性來切換:
<script setup>
import { ref } from 'vue'
const isVisible = ref(true)
</script>
<template>
<p v-show="isVisible">這段文字可以顯示/隱藏</p>
<button @click="isVisible = !isVisible">切換</button>
</template>
v-if vs v-show
| 特性 | v-if | v-show |
|---|---|---|
| 切換方式 | 新增/移除 DOM 元素 | 切換 CSS display 屬性 |
| 初始渲染成本 | 條件為假時不渲染 | 無論條件都會渲染 |
| 切換成本 | 較高(重新建立元素) | 較低(只改 CSS) |
| 適用場景 | 條件很少改變 | 需要頻繁切換 |
v-show 不支援在 <template> 上使用,也不能和 v-else 搭配。v-for - 列表渲染
v-for 用於渲染列表:
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, text: '學習 Vue' },
{ id: 2, text: '建立專案' },
{ id: 3, text: '部署應用' }
])
const user = ref({
name: 'John',
age: 30,
city: 'Taipei'
})
</script>
<template>
<!-- 遍歷陣列 -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</ul>
<!-- 取得索引 -->
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index + 1 }}. {{ item.text }}
</li>
</ul>
<!-- 遍歷物件 -->
<ul>
<li v-for="(value, key) in user" :key="key">
{{ key }}: {{ value }}
</li>
</ul>
<!-- 遍歷數字範圍 -->
<span v-for="n in 5" :key="n">{{ n }} </span>
<!-- 輸出:1 2 3 4 5 -->
</template>
v-for 時一定要提供 :key 屬性,且 key 值必須是唯一的。這有助於 Vue 追蹤每個節點的身份,從而重用和重新排序現有元素。v-for 與 v-if
不建議在同一元素上同時使用 v-for 和 v-if。如果需要過濾列表,建議使用 computed 屬性:
<script setup>
import { ref, computed } from 'vue'
const todos = ref([
{ id: 1, text: '學習 Vue', done: true },
{ id: 2, text: '建立專案', done: false },
{ id: 3, text: '部署應用', done: false }
])
// 使用 computed 過濾
const incompleteTodos = computed(() => {
return todos.value.filter(todo => !todo.done)
})
</script>
<template>
<ul>
<li v-for="todo in incompleteTodos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</template>
更多列表渲染的用法請參考 列表渲染。
v-slot - 插槽
v-slot 用於定義和使用插槽內容,簡寫為 #:
<!-- 子元件 BaseLayout.vue -->
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父元件使用 -->
<template>
<BaseLayout>
<template v-slot:header>
<h1>頁面標題</h1>
</template>
<!-- 預設插槽 -->
<p>主要內容</p>
<template #footer> <!-- 簡寫語法 -->
<p>頁尾資訊</p>
</template>
</BaseLayout>
</template>
更多插槽的用法請參考 Slots 插槽。
v-pre
v-pre 會跳過這個元素及其子元素的編譯過程,直接顯示原始的 Mustache 標籤:
<template>
<!-- 會顯示 {{ message }} 而不是值 -->
<span v-pre>{{ message }}</span>
</template>
這在需要顯示原始 Mustache 語法的說明文件時很有用。
v-once
v-once 只會渲染元素一次,之後的重新渲染會跳過這個元素及其子元素:
<script setup>
import { ref } from 'vue'
const message = ref('Hello')
</script>
<template>
<!-- 即使 message 改變,也不會更新 -->
<p v-once>{{ message }}</p>
<!-- 這個會隨著 message 更新 -->
<p>{{ message }}</p>
<button @click="message = 'Changed'">修改</button>
</template>
這可以用於優化效能,避免不需要更新的內容重新渲染。
v-memo(Vue 3.2+)
v-memo 用於記憶模板的子樹,只有當依賴的值改變時才會重新渲染:
<script setup>
import { ref } from 'vue'
const items = ref([/* 大量資料 */])
const selected = ref(null)
</script>
<template>
<div v-for="item in items" :key="item.id" v-memo="[item.id === selected]">
<p>{{ item.name }}</p>
<p :class="{ active: item.id === selected }">
{{ item.id === selected ? '已選中' : '' }}
</p>
</div>
</template>
v-memo 接收一個陣列,只有當陣列中的任一值改變時,才會更新該元素。這對於大型列表的效能優化很有幫助。
v-cloak
v-cloak 用於隱藏尚未編譯完成的模板,防止使用者看到原始的 Mustache 標籤閃爍:
<style>
[v-cloak] {
display: none;
}
</style>
<template>
<div v-cloak>
{{ message }}
</div>
</template>
編譯完成後,v-cloak 屬性會被移除。這在使用 CDN 引入 Vue 時特別有用,因為模板可能會在 Vue 載入前就顯示。
自訂指令
除了內建指令,Vue 也允許你建立自訂指令。詳細內容請參考 自訂指令。