CHAPTER 07 / FRAMEWORK
Vue:
學習曲線最溫和的框架
React 的姊妹競品。台灣很多公司用 Vue,中文資源豐富、上手最快。 你看 React 章節覺得「JSX 怎麼這麼怪」的話,Vue 用單檔元件 (SFC),HTML 歸 HTML、JS 歸 JS、CSS 歸 CSS,舒服得多。
- 看懂任何 Vue 3 專案
- 會用 ref、reactive、computed
- 會寫單檔元件 (.vue)
- 會用 Pinia 管全域狀態
- 準備好寫 Nuxt 全端
LESSON 7.1
Vue 跟 React 差在哪?
| Vue 3 | React | |
|---|---|---|
| 檔案 | .vue 單檔元件 | .tsx 全寫 JS |
| 模板 | HTML + 指令(v-if、v-for) | JSX |
| 狀態 | ref / reactive 自動追蹤 | useState 手動 setter |
| 學習曲線 | 緩 | 陡 |
| 市佔 | 中 | 高(全球第一) |
| 台灣職缺 | 不少 | 較多 |
開新專案
npm create vue@latest my-vue-app
cd my-vue-app
npm install
npm run dev
LESSON 7.2
單檔元件 (SFC)
<script setup lang="ts">
import { ref } from 'vue';
const count = ref(0);
const name = ref('小明');
function inc() {
count.value++;
}
</script>
<template>
<div class="box">
<h1>哈囉 {{ name }}</h1>
<p>計數: {{ count }}</p>
<button @click="inc">+</button>
</div>
</template>
<style scoped>
.box { padding: 20px; }
</style>
三個區塊:<script>、<template>、<style scoped>。scoped 表示這個 CSS 只影響這個元件。
React 是把所有東西丟一個果汁機打成 JS。Vue 像便當:飯一格、菜一格、肉一格,分開放但是是同個盒子。
LESSON 7.3
ref 跟 reactive
import { ref, reactive, computed } from 'vue';
// ref:原始型別、單值
const count = ref(0);
count.value++; // JS 裡要 .value
// 在 template 裡寫 {{ count }} 不用 .value
// reactive:物件、陣列
const user = reactive({ name: '小明', age: 15 });
user.age = 16; // 不用 .value
// computed:自動算的衍生值
const double = computed(() => count.value * 2);
實務上多數情況都用 ref,比較一致。reactive 有解構陷阱(const { age } = user 後 age 失去響應)。
LESSON 7.4
模板指令
<template>
<!-- 文字綁定 -->
<p>{{ message }}</p>
<!-- 屬性綁定 -->
<img :src="imgUrl" :alt="imgAlt">
<!-- 事件 -->
<button @click="handleClick">送出</button>
<!-- 條件 -->
<p v-if="loggedIn">歡迎</p>
<p v-else>請登入</p>
<!-- 顯示/隱藏(保留 DOM) -->
<div v-show="open">...</div>
<!-- 迴圈 -->
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
<!-- 雙向綁定(神技) -->
<input v-model="username">
<!-- class / style -->
<div :class="{ active: isActive, error: hasError }"></div>
<div :style="{ color: textColor }"></div>
</template>
v-model 是 Vue 殺手鐧。React 要寫 value={x} onChange={e => setX(e.target.value)},Vue 一個 v-model="x" 解決。
LESSON 7.5
元件與 Props / Emit
<!-- Card.vue -->
<script setup lang="ts">
defineProps<{
title: string;
desc: string;
}>();
const emit = defineEmits<{
click: [id: number];
}>();
</script>
<template>
<div @click="emit('click', 1)">
<h3>{{ title }}</h3>
<p>{{ desc }}</p>
</div>
</template>
<!-- 使用 -->
<Card title="標題" desc="描述" @click="handleClick" />
LESSON 7.6
生命週期 & watch
import { onMounted, onUnmounted, watch } from 'vue';
onMounted(() => {
console.log('元件出現了');
fetchData();
});
onUnmounted(() => {
clearInterval(timerId);
});
// 監聽變化
watch(userId, (newId, oldId) => {
fetchUser(newId);
});
// 監聽多個
watch([a, b], ([newA, newB]) => { ... });
LESSON 7.7
Pinia:全域狀態管理
npm install pinia
// stores/counter.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
const double = computed(() => count.value * 2);
function inc() { count.value++; }
return { count, double, inc };
});
// 任何元件用
const counter = useCounterStore();
counter.inc();
console.log(counter.count, counter.double);
練習:Vue 版 To-Do List
把 React 版重寫成 Vue 3:
- App.vue 包三個子元件
- 用
v-model綁輸入框 - 用 Pinia store 存所有 todos + filter 狀態
- 用
computed算「未完成數量」 - 用
<Transition>加入動畫
常見卡關 FAQ
// Vue 學員最常問的問題
為什麼 ref 要寫 .value?
JS 沒有 React 的 hook 機制,Vue 用 Proxy 追蹤 ref。
.value 是觸發 getter/setter 的方式。template 裡 Vue 自動幫你 unwrap,所以不用寫。Options API 跟 Composition API 學哪個?
學 Composition API(
<script setup>)。Vue 3 的主推、跟 React Hooks 思維接近、TS 體驗好。Options API 維護舊專案再學。v-if 跟 v-show 用哪個?
頻繁切換用
v-show(只切 CSS display)。一次性條件用 v-if(DOM 真的不渲染)。解構 reactive 後失效?
是的!
const { x } = reactive({x:1}) x 就失去響應。要 const { x } = toRefs(reactive({x:1})),或一開始就用 ref。Vuex 還是 Pinia?
新專案必選 Pinia。它是 Vuex 5。Vue 官方推薦。Vuex 維護舊專案才碰。