本文 AI 產出,尚未審核

Vue 3 基礎概念(Fundamentals)

Vue 是什麼、MVVM 架構原理


簡介

在前端開發的浪潮中,Vue.js 以其輕量、易上手、彈性高的特性,成為許多開發團隊的首選框架。Vue 3 更在效能、組件化以及 TypeScript 支援上大幅提升,讓開發者可以用更少的程式碼寫出更健全的應用。

了解 Vue 是什麼、以及它背後的 MVVM(Model‑View‑ViewModel) 架構,是學習任何前端框架的根本。掌握這些概念後,你才能在實務開發中正確設計資料流、避免不必要的重繪,並寫出易於維護的程式碼。

本文將從概念說明、程式碼範例、常見陷阱與最佳實踐,最後延伸到真實的應用場景,幫助初學者到中階開發者快速建立 Vue 3 的基礎認知。


核心概念

1. Vue 是什麼?

Vue 是一套 漸進式 的前端框架,核心只提供響應式資料綁定組件系統,其餘功能(路由、狀態管理、測試等)則以插件形式提供。這樣的設計讓你可以:

  • 只在單一頁面加入 <script src="https://unpkg.com/vue@3"></script> 即可使用
  • 在大型專案中使用 Vue CLI / Vite 建立完整的開發環境

2. MVVM 架構原理

層級 角色 主要職責
Model 資料層 負責儲存與管理應用的狀態(例如 API 回傳的 JSON)
View UI 層 呈現使用者介面,直接綁定 Model 的資料
ViewModel 中介層 透過 雙向綁定v-model)或 單向綁定{{ }})將 Model 與 View 連結起來,同時處理 UI 事件

在 Vue 中,ViewModel 就是 Vue 實例(或組件),它透過 reactivity 系統 監控 Model 的變化,當資料改變時自動觸發 View 的重新渲染,實現 資料驅動 UI

3. 响應式系統(Reactivity)

Vue 3 使用 Proxy 取代 Vue 2 的 Object.defineProperty,使得:

  • 嵌套物件陣列 的變化都能被正確追蹤
  • 性能 更佳,因為 Proxy 能一次捕捉所有屬性的讀寫操作

3.1 reactiveref

  • reactive:適用於 物件,返回一個 Proxy 包裝的物件。
  • ref:適用於 原始值(string、number、boolean)或需要解構的情況,返回一個帶有 .value 的引用。
import { reactive, ref, computed } from 'vue';

// reactive 範例:深層物件皆可追蹤
const user = reactive({
  name: 'Alice',
  age: 25,
  address: {
    city: 'Taipei',
    zip: '100'
  }
});

// ref 範例:單一值或需要解構的情況
const counter = ref(0);

// computed:基於 reactive / ref 計算衍生值
const greeting = computed(() => `Hello, ${user.name}!`);

4. 組件化(Component)

Vue 的組件是一個 可重用的 UI 單位,每個組件都有自己的 template、script、style。組件的生命週期、props、emit 等概念,使得 UI 可以 模組化、解耦合

<!-- HelloWorld.vue -->
<template>
  <h1>{{ msg }}</h1>
  <button @click="count++">點擊次數:{{ count }}</button>
</template>

<script setup>
import { ref } from 'vue';
defineProps({ msg: String });

const count = ref(0);
</script>

<style scoped>
h1 { color: #42b983; }
</style>

5. 單向 vs 雙向綁定

  • 單向綁定{{ }}v-bind):資料從 Model 流向 View,適合顯示性內容。
  • 雙向綁定v-model):資料同時在 View 與 Model 之間同步,常用於表單輸入。
// 單向綁定範例
<div>{{ message }}</div>

// 雙向綁定範例(表單)
<input v-model="form.username" placeholder="請輸入帳號" />

程式碼範例

以下提供 5 個實用範例,說明 Vue 3 在不同情境下的使用方式。

範例 1:簡易計數器(使用 ref

import { createApp, ref } from 'vue';

createApp({
  setup() {
    const count = ref(0);
    const increment = () => count.value++;

    return { count, increment };
  },
  template: `
    <button @click="increment">點擊 +1</button>
    <p>目前計數:{{ count }}</p>
  `
}).mount('#app');

說明ref 讓原始值具備響應式,count.value 的變化會自動更新畫面。

範例 2:使用 reactive 管理表單資料

import { createApp, reactive } from 'vue';

createApp({
  setup() {
    const form = reactive({
      name: '',
      email: ''
    });

    const submit = () => {
      console.log('送出資料:', { ...form });
      // 這裡可呼叫 API
    };

    return { form, submit };
  },
  template: `
    <form @submit.prevent="submit">
      <label>姓名:<input v-model="form.name" /></label><br/>
      <label> Email:<input v-model="form.email" /></label><br/>
      <button type="submit">送出</button>
    </form>
  `
}).mount('#app');

重點reactive 讓整個物件保持響應式,v-model 直接綁定子屬性。

範例 3:計算屬性(computed)與監聽(watch

import { createApp, reactive, computed, watch } from 'vue';

createApp({
  setup() {
    const state = reactive({
      price: 1200,
      quantity: 2
    });

    const total = computed(() => state.price * state.quantity);

    // 監聽 total 變化,示範副作用處理
    watch(total, (newVal, oldVal) => {
      console.log(`總金額從 ${oldVal} 變成 ${newVal}`);
    });

    return { state, total };
  },
  template: `
    <div>
      單價:<input type="number" v-model.number="state.price" /><br/>
      數量:<input type="number" v-model.number="state.quantity" /><br/>
      <strong>總金額:{{ total }}</strong>
    </div>
  `
}).mount('#app');

說明computed 只在依賴變更時重新計算,watch 用於執行副作用(如 API 呼叫、log)。

範例 4:父子組件溝通(Props + Emit)

<!-- Parent.vue -->
<template>
  <Counter :initial="5" @update="handleUpdate" />
  <p>子組件目前值:{{ childValue }}</p>
</template>

<script setup>
import { ref } from 'vue';
import Counter from './Counter.vue';

const childValue = ref(0);
const handleUpdate = (val) => {
  childValue.value = val;
};
</script>
<!-- Counter.vue -->
<template>
  <button @click="decrease">-</button>
  {{ count }}
  <button @click="increase">+</button>
</template>

<script setup>
import { ref, watch, defineProps, defineEmits } from 'vue';

const props = defineProps({ initial: Number });
const emit = defineEmits(['update']);

const count = ref(props.initial ?? 0);

const increase = () => count.value++;
const decrease = () => count.value--;

// 每次 count 改變時向父層發射事件
watch(count, (val) => emit('update', val));
</script>

重點:使用 props 接收父層資料,emit 向父層回傳變更,形成單向資料流。

範例 5:動態掛載組件(<component :is>

import { createApp, ref, defineAsyncComponent } from 'vue';

const Home = { template: '<h2>首頁</h2>' };
const About = defineAsyncComponent(() => import('./About.vue'));

createApp({
  setup() {
    const current = ref('home'); // home | about

    const switchView = (view) => current.value = view;

    return { current, switchView };
  },
  template: `
    <nav>
      <button @click="switchView('home')">Home</button>
      <button @click="switchView('about')">About</button>
    </nav>
    <component :is="current === 'home' ? Home : About"></component>
  `,
  components: { Home, About }
}).mount('#app');

說明<component :is> 可以根據狀態切換不同組件,配合 lazy loadingdefineAsyncComponent)提升首屏載入速度。


常見陷阱與最佳實踐

陷阱 可能的結果 建議的解決方案
直接修改 Props Vue 警告「Attempting to mutate a prop directly」並破壞單向資料流 使用 本地副本const local = ref(props.xxx))或 emit 事件回傳變更
非響應式的物件屬性(例如直接 obj.newProp = 1 新屬性不會觸發更新 使用 reactive 時,set 新屬性前先 obj = {...obj, newProp: 1},或使用 Vue.set(Vue 2)/ reactiveproxy 自動偵測
setup 中使用 this thisundefined,導致錯誤 直接使用返回的變數或 context,不要依賴 this
大量資料一次性寫入(如大量陣列 push) 多次重繪影響效能 使用 批次更新nextTick)或 splice 替代多次 push
忘記在模板中使用 .value(使用 ref 顯示為 [object Object] 或空值 在模板中 自動解包,但在 script 中需使用 .value

最佳實踐

  1. 盡量使用 Composition API (setup):邏輯更易拆分、重複使用。
  2. 保持單向資料流:父層提供 props,子層只透過 emit 回傳。
  3. 利用 computed 取代不必要的 watch:計算屬性是更簡潔的衍生資料寫法。
  4. 懶載入大型組件defineAsyncComponent 配合路由分割點,減少首屏載入時間。
  5. 使用 TypeScript:Vue 3 原生支援,能在開發階段捕捉錯誤,提升可維護性。

實際應用場景

場景 為何選擇 Vue 3 典型實作方式
企業內部儀表板 需要 即時更新、大量圖表、表單驗證 使用 reactive 管理全域狀態,computed 計算統計值,watch 監聽 API 回傳
行動電商前端 SEOSSR(Nuxt)與 SPA 兼容 在 Nuxt 3 中使用 Vue 3 的 Composition API,搭配 pinia 做全域購物車
中小型 SaaS 產品 快速開發組件庫(如 Element Plus) 建立可重用的 UI 組件,利用 props + emit 組成表單套件
單頁式管理系統 路由分割權限控管 使用 Vue Router + defineAsyncComponent 動態載入功能模組,配合 navigation guards 進行權限檢查
即時聊天應用 雙向綁定WebSocket 整合 ref 保存訊息列表,watchEffect 監聽 WebSocket 推送,computed 計算未讀數量

總結

Vue 3 以 Proxy 為核心的響應式系統Composition API 以及 組件化 為基礎,完美落實 MVVM 的資料驅動理念。掌握 reactiverefcomputedwatch 以及 props / emit 的正確使用方式,能讓你在開發過程中:

  • 減少手動 DOM 操作,讓 UI 自動跟隨資料變化
  • 提升可維護性:邏輯分離、組件重用、單向資料流
  • 優化效能:懶載入、批次更新、最小化重繪

在實務上,從簡易計數器到企業級儀表板,Vue 3 都能提供彈性且高效的開發體驗。只要遵循 最佳實踐,避開常見陷阱,你就能在短時間內構建出 可擴展、易維護 的前端應用。

祝你在 Vue 3 的旅程中收穫滿滿,寫出更快、更好、更有品質的程式碼! 🚀