Vue 3 模板語法(Template Syntax)

插值(Interpolation)

僅能用於插入純文字,若需綁定 HTML 屬性請使用 v-bind

<p>{{ message }}</p>
<script setup>
import { ref } from "vue";
const message = ref("Hello Vue 3!");
</script>

屬性綁定(v-bind)

<img v-bind:src="imageUrl" alt="圖片">
<!-- 簡寫 -->
<img :src="imageUrl" alt="圖片">
<script setup>
import { ref } from "vue";
const imageUrl = ref("https://example.com/image.jpg");
</script>

事件監聽(v-on)

<button v-on:click="increment">增加</button>
<!-- 簡寫 -->
<button @click="increment">增加</button>
<script setup>
import { ref } from "vue";
const count = ref(0);
const increment = () => count.value++;
</script>

事件修飾符

<!-- 事件僅觸發一次 -->
<button @click.once="handleClick">點我一次</button>

<!-- 阻止事件冒泡 -->
<button @click.stop="handleClick">阻止冒泡</button>

<!-- 阻止預設行為 -->
<form @submit.prevent="submitForm">提交</form>

雙向綁定(v-model)

<input v-model="name" placeholder="請輸入姓名">
<p>你的姓名是:{{ name }}</p>
<script setup>
import { ref } from "vue";
const name = ref("");
</script>

支援元素:

<textarea v-model="message"></textarea>

<select v-model="selected">
  <option value="A">A</option>
  <option value="B">B</option>
</select>

<input type="checkbox" v-model="checked">

條件渲染(v-if / v-show)

<p v-if="isVisible">這段文字會根據條件顯示</p>
<p v-else>這是 else 區塊</p>

<p v-show="isVisible">這段文字使用 v-show</p>
<script setup>
import { ref } from "vue";
const isVisible = ref(true);
</script>

列表渲染(v-for)

陣列範例:

<ul>
  <li v-for="(item, index) in items" :key="index">
    {{ index }} - {{ item }}
  </li>
</ul>
<script setup>
import { ref } from "vue";
const items = ref(["蘋果", "香蕉", "橘子"]);
</script>

物件範例:

<ul>
  <li v-for="(value, key) in user" :key="key">
    {{ key }}: {{ value }}
  </li>
</ul>
<script setup>
const user = {
  name: "Jack",
  age: 25,
  city: "Taipei"
};
</script>

計算屬性(computed)

<p>雙倍數值:{{ doubleCount }}</p>
<script setup>
import { ref, computed } from "vue";
const count = ref(5);
const doubleCount = computed(() => count.value * 2);
</script>

監聽器(watch)

<script setup>
import { ref, watch } from "vue";
const name = ref("");

watch(name, (newVal, oldVal) => {
  console.log(`name 改變:${oldVal} -> ${newVal}`);
});
</script>

自動依賴監聽(watchEffect)

<script setup>
import { ref, watchEffect } from "vue";
const count = ref(0);

watchEffect(() => {
  console.log(`count: ${count.value}`);
});
</script>

特性比較

特性 watchEffect watch
是否指定變數 ❌ 自動收集 ✅ 明確指定
是否立即執行 ✅ 是 ❌ 否
適用情境 動態收集依賴 精確監聽變數

監聽多個變數

<script setup>
import { ref, watchEffect } from "vue";
const a = ref(1);
const b = ref(2);

watchEffect(() => {
  console.log(`a: ${a.value}, b: ${b.value}`);
});
</script>

清除副作用

<script setup>
import { ref, watchEffect } from "vue";
const count = ref(0);

watchEffect((onCleanup) => {
  const timer = setInterval(() => {
    console.log(`count: ${count.value}`);
  }, 1000);

  onCleanup(() => {
    clearInterval(timer);
    console.log("計時器清除");
  });
});
</script>

停止監聽

<script setup>
import { ref, watchEffect } from "vue";
const count = ref(0);

const stop = watchEffect(() => {
  console.log(`count: ${count.value}`);
});

setTimeout(() => {
  stop(); // 停止監聽
  console.log("watchEffect 停止");
}, 5000);
</script>

元件傳值(Props & Emits)

Props:父傳子

<!-- Parent.vue -->
<ChildComponent :message="parentMessage" />
<!-- ChildComponent.vue -->
<template>
  <p>{{ message }}</p>
</template>

<script setup>
defineProps(["message"]);
</script>
<script setup>
import ChildComponent from "./ChildComponent.vue";
import { ref } from "vue";
const parentMessage = ref("Hello from Parent");
</script>

Emits:子傳父

<!-- Parent.vue -->
<ChildComponent @update-message="handleUpdateMessage" />
<p>來自子元件的消息:{{ childMessage }}</p>
<!-- ChildComponent.vue -->
<template>
  <button @click="sendMessage">傳送消息</button>
</template>

<script setup>
const emit = defineEmits(["update-message"]);

const sendMessage = () => {
  emit("update-message", "來自子元件的訊息");
};
</script>
<script setup>
import ChildComponent from "./ChildComponent.vue";
import { ref } from "vue";
const childMessage = ref("");

const handleUpdateMessage = (msg) => {
  childMessage.value = msg;
};
</script>