本文 AI 產出,尚未審核

Vue 3 基礎概念:Vue 2 與 Vue 3 的差異

簡介

隨著前端框架的快速演進,Vue.js 從 2.x 版升級到 3.x 版已成為許多開發團隊的必經之路。Vue 3 不僅在效能上有顯著提升,更引入了全新的 Composition API、更好的 TypeScript 支援以及更靈活的插件機制。對於已經熟悉 Vue 2 的開發者來說,了解兩個版本的差異是平滑遷移、寫出更可維護程式碼的關鍵。

本篇文章將從語法、生命週期、響應式系統、組件註冊、編譯與部署等多個面向,逐項比較 Vue 2 與 Vue 3 的變化,並提供實用的程式碼範例,協助你在實務專案中快速上手 Vue 3。


核心概念

1. 全新的 Composition API

Vue 2 主要依賴 Options APIdatamethodscomputedwatch 等),隨著組件變得複雜,邏輯會散落在不同的選項中,難以重用。Vue 3 引入 Composition APIsetuprefreactivecomputedwatchEffect 等),讓開發者可以把相關邏輯「組合」在同一個函式內。

// Vue 2 - Options API
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    inc() { this.count++ }
  },
  computed: {
    double() { return this.count * 2 }
  }
}
// Vue 3 - Composition API
import { ref, computed } from 'vue'

export default {
  setup() {
    const count = ref(0)               // 响应式引用
    const inc = () => count.value++    // 方法
    const double = computed(() => count.value * 2) // 计算属性

    return { count, inc, double }      // 暴露給模板使用
  }
}

重點setup 只在組件 創建前 執行一次,返回的屬性會自動注入模板。這使得相關的狀態與行為可以在同一塊程式碼中看到,提升可讀性與可測試性。

2. 改善的響應式系統(Proxy vs Object.defineProperty)

Vue 2 使用 Object.defineProperty 逐屬性劫持,對於深層嵌套的物件或陣列會產生性能瓶頸。Vue 3 改採 ES6 Proxy,一次性代理整個物件,支援 更完整的偵測(包括新增/刪除屬性)且效能提升約 2~3 倍

// Vue 2 - 只能偵測已存在的屬性
this.$set(this.obj, 'newProp', 123)   // 必須手動 set
// Vue 3 - Proxy 自動偵測
const state = reactive({ a: 1 })
state.b = 2   // 新增屬性即被追蹤

實務建議:在 Vue 3 中直接使用 reactiveref,不需要再使用 Vue.setthis.$set

3. 更好的 TypeScript 支援

Vue 2 的 TypeScript 支援主要靠 vue-class-componentvue-property-decorator,寫法較為冗長。Vue 3 從設計階段即考慮 TypeScript,提供 內建型別defineComponentPropType 等工具讓型別推斷變得自然。

// Vue 3 + TypeScript
import { defineComponent, PropType } from 'vue'

export default defineComponent({
  props: {
    msg: {
      type: String as PropType<string>,
      required: true
    },
    list: {
      type: Array as PropType<number[]>,
      default: () => []
    }
  },
  setup(props) {
    // props 已被正確推斷為 { msg: string; list: number[] }
    return {}
  }
})

要點:使用 defineComponent 包裹組件,IDE 能即時提供屬性、事件的型別提示。

4. 生命週期 Hook 的變更

Vue 3 為了讓 Composition API 更直觀,將生命週期 Hook 改為 函式形式onMountedonBeforeUnmount 等),可在 setup 中直接呼叫。

Vue 2 Vue 3 (Composition)
created onCreated (已廢除)
mounted onMounted
updated onUpdated
destroyed onBeforeUnmount / onUnmounted
// Vue 2
export default {
  mounted() {
    console.log('mounted')
  }
}
// Vue 3 - Composition
import { onMounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('mounted')
    })
  }
}

提醒onBeforeMountonMounted 只能在 setup 中使用;若仍想使用 Options API,仍能保留原本的生命週期寫法。

5. 組件註冊與全局 API 的改寫

Vue 3 把 全局 API(如 Vue.componentVue.use)移至 app 實例createApp 返回的物件),讓多個 Vue 應用可以共存於同一頁面。

// Vue 2
import Vue from 'vue'
import MyComponent from './MyComponent.vue'

Vue.component('my-component', MyComponent)
new Vue({ el: '#app' })
// Vue 3
import { createApp } from 'vue'
import App from './App.vue'
import MyComponent from './MyComponent.vue'

const app = createApp(App)
app.component('my-component', MyComponent) // 全局註冊
app.mount('#app')

最佳實踐:盡量使用 局部註冊(在父組件 components 中引用)以減少全局污染,提升程式碼可維護性。

6. 編譯與部署:更小的體積與更快的載入

Vue 3 的核心程式碼體積約 50%(約 20KB gzipped),主要得益於:

  • Tree‑shaking:模組化設計允許 bundler(如 Vite、Webpack)剔除未使用的 API。
  • Fragment、Teleport、Suspense:原生支援多根節點、跨層級渲染與異步組件,減少額外的 wrapper 元素。
# Vue 2
npm install vue@2
# Vue 3
npm install vue@3

實務提示:若使用 Vite 作為開發伺服器,Vue 3 的即時模組熱重載(HMR)速度比 Vue 2 + webpack 更快。


常見陷阱與最佳實踐

陷阱 可能的情況 解決方式
忘記使用 .value ref 中讀寫時直接使用變數(例如 count = 1 使用 count.value,或在 setup 中透過 toRefs 轉成普通屬性
混用 Options API 與 Composition API 同一組件內同時使用 datasetup,導致狀態不一致 建議 統一 使用一種 API;若必須混用,確保資料來源單一
Proxy 無法偵測陣列索引變化(舊版瀏覽器) 在 IE11 或舊版 Safari 中使用 Vue 3 需使用 polyfill@vue/reactivity)或降級至 Vue 2
全局 API 仍使用 Vue 2 方式 Vue.use(plugin) 直接調用 改為 app.use(plugin),並在 createApp 後執行
組件懶載入寫法錯誤 使用 import() 但忘記包裝 defineAsyncComponent javascript\nimport { defineAsyncComponent } from 'vue'\nconst AsyncComp = defineAsyncComponent(() => import('./MyComp.vue'))\n

最佳實踐

  1. 優先使用 Composition API:對於可重用的業務邏輯,寫成 useXxx Hook(如 useFetch),在不同組件間直接引用。
  2. 善用 TypeScript:在 propsemitexpose 中明確聲明型別,讓 IDE 能提供即時錯誤檢查。
  3. 拆分大型組件:利用 Fragment 省去不必要的 <template> 包裹,減少 DOM 層級。
  4. 使用 Teleport 處理全局 UI(如 Modal、Toast),避免在根組件中堆疊大量結構。
  5. 在測試環境使用 Vue Test Utils v2:它已支援 Vue 3 的新特性,避免因版本不匹配導致測試失敗。

實際應用場景

場景 Vue 2 實作方式 Vue 3 改寫建議
表單驗證 使用 data + watch,每個欄位寫獨立驗證函式 使用 Composition API 把驗證邏輯抽成 useForm,在不同表單共用
即時搜尋 created 中發起 API,watch 監控搜尋字串 使用 ref + watchEffect,自動在字串變更時重新發送請求
全局狀態管理 Vuex 3(依賴 Object.defineProperty Vuex 4(與 Vue 3 完全相容)或 Pinia(基於 Proxy,開箱即支援 TypeScript)
大型儀表板 多層嵌套的 <template>,大量 <div> 包裝 使用 Fragment 減少不必要的包裝,使用 Suspense 讓子圖表懶載入
跨層級彈窗 透過 $root 事件總線傳遞 使用 Teleport 把彈窗直接渲染到 body,避免層級限制

範例:使用 Teleport 建立全局 Modal

// Modal.vue
<template>
  <teleport to="body">
    <div class="overlay" v-if="visible" @click.self="close">
      <div class="modal">
        <slot />
      </div>
    </div>
  </teleport>
</template>

<script setup>
import { ref, defineExpose } from 'vue'

const visible = ref(false)
const open = () => (visible.value = true)
const close = () => (visible.value = false)

defineExpose({ open, close })
</script>

<style scoped>
.overlay { /* 半透明背景 */ }
.modal   { /* 中央對話框 */ }
</style>
// 在其他組件中使用
import Modal from './Modal.vue'
const modalRef = ref(null)

function showInfo() {
  modalRef.value?.open()
}

總結

Vue 3 相較於 Vue 2 在 效能、可組合性、TypeScript 支援與生態系統 上都有顯著突破。核心差異包括:

  • Composition API 取代或補足 Options API,使邏輯更易重用。
  • Proxy 為基礎的響應式系統,提升偵測能力與效能。
  • 全局 API 移至 app 實例,支援多應用同頁共存。
  • 更完善的 TypeScript 整合,減少手動類型宣告。
  • Fragment、Teleport、Suspense 等新特性,讓 UI 結構更彈性。

在實務開發中,建議先 從小模組開始練習 Composition API,逐步將大型組件遷移,並搭配 PiniaVuex 4 管理全局狀態。透過上述的差異說明與範例,你已具備從 Vue 2 平滑過渡至 Vue 3 的基礎知識,未來可以更自信地在新專案中運用 Vue 3 的強大功能,打造效能卓越且易於維護的前端應用。祝開發順利!