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 3React
檔案.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:

  1. App.vue 包三個子元件
  2. v-model 綁輸入框
  3. 用 Pinia store 存所有 todos + filter 狀態
  4. computed 算「未完成數量」
  5. <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 維護舊專案才碰。
← 上一章
07 React