Vue 3 基礎概念 — 單檔元件(Single File Component, .vue)
簡介
在 Vue 生態系統中,**單檔元件(Single File Component,簡稱 SFC)**是最常見、也是最推薦的開發方式。它將 template、script、style 三大部份封裝在同一個 .vue 檔案中,讓開發者可以在同一個檔案內完成 UI 標記、行為邏輯與樣式設計,提升可讀性、可維護性與團隊協作效率。
Vue 3 對 SFC 做了多項優化(如 <script setup>、<style scoped>、<template> 的編譯快取等),使得開發體驗更流暢、程式碼更簡潔。掌握 SFC 的寫法與最佳實踐,對於從 Vue 2 轉向 Vue 3,或是剛踏入前端框架的開發者,都相當重要。
本篇文章將以 淺顯易懂 的方式,從基本結構說起,逐步帶出實務上常用的技巧與陷阱,幫助你快速上手並寫出高品質的 Vue 3 單檔元件。
核心概念
1. SFC 的基本檔案結構
<!-- MyComponent.vue -->
<template>
<!-- HTML 標記 -->
<div class="card">
<h2>{{ title }}</h2>
<p>{{ message }}</p>
<button @click="increment">點我 +1</button>
<span>計數:{{ count }}</span>
</div>
</template>
<script>
export default {
name: 'MyComponent', // 元件名稱
props: {
title: String,
message: { type: String, default: 'Hello Vue 3!' }
},
data() {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
}
}
};
</script>
<style scoped>
.card {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
margin-top: 0.5rem;
}
</style>
<template>:放置 HTML 結構,支援所有 Vue 模板語法。<script>:寫元件的 JavaScript(或 TypeScript)邏輯。<style>:元件專屬的 CSS,使用scoped可避免樣式洩漏。
Tip:在同一個
.vue檔案內,只能有一個<template>、一個<script>(或多個<script setup>)以及任意多個<style>。
2. <script setup>:最簡潔的組件寫法
Vue 3.2 之後推出的 <script setup> 語法,讓開發者不必再寫 export default {},直接在檔案頂層宣告變數、函式,即可自動成為組件的 setup 內容。
<!-- Counter.vue -->
<template>
<button @click="increment">點我 +1({{ count }})</button>
</template>
<script setup>
import { ref } from 'vue';
// 定義響應式資料
const count = ref(0);
// 方法直接寫成函式
function increment() {
count.value++;
}
</script>
<style scoped>
button {
font-size: 1rem;
padding: 0.5rem 1rem;
}
</style>
ref用於建立基本類型的響應式資料。reactive(稍後會介紹)則適合物件/陣列。- 所有在
<script setup>中聲明的變數,都會自動在模板中可見,不需要this。
3. Props、Emits 與 TypeScript
在 SFC 中使用 defineProps、defineEmits,可以在 <script setup> 中聲明 傳入屬性(props) 與 自訂事件(emits),結合 TypeScript 時更能提供型別安全。
<!-- Greeting.vue -->
<template>
<h1 @click="sayHello">{{ name }},歡迎光臨!</h1>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue';
// Props 型別
interface Props {
name: string;
age?: number;
}
const props = defineProps<Props>();
// Emits 型別
const emit = defineEmits<{
(e: 'greet', payload: string): void;
}>();
function sayHello() {
emit('greet', `Hello, ${props.name}!`);
}
</script>
<style scoped>
h1 {
cursor: pointer;
color: #42b983;
}
</style>
lang="ts"讓編輯器自動啟用 TypeScript 語法檢查。defineProps、defineEmits皆返回對應的型別,避免在使用時出現型別錯誤。
4. Scoped CSS 與 CSS Modules
scoped 會在編譯階段為每個元素自動加上唯一的屬性選擇器,確保樣式僅作用於當前元件;若需要更嚴格的模組化管理,Vue 也支援 CSS Modules。
<!-- Card.vue -->
<template>
<section :class="$style.card">
<slot />
</section>
</template>
<script setup>
</script>
<style module>
.card {
border: 1px solid #e0e0e0;
padding: 1rem;
border-radius: 6px;
}
</style>
- 使用
$style取得模組化的 class 名稱,不會與其他元件衝突。 module取代scoped,提供更細緻的命名空間。
5. 動態匯入與 Lazy Loading
在大型專案中,按需載入 元件能顯著減少首屏載入時間。Vue 3 支援 defineAsyncComponent 或直接使用動態 import()。
<!-- AsyncWrapper.vue -->
<template>
<Suspense>
<template #default>
<LazyComponent />
</template>
<template #fallback>
<div>載入中…</div>
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
const LazyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
);
</script>
Suspense會在子元件尚未解決前顯示 fallback,提升使用者體驗。defineAsyncComponent內部會自動處理錯誤、重試等機制。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
忘記加 scoped |
樣式會洩漏到全局,造成意外的 UI 變化。 | 在 <style> 標籤加上 scoped,或改用 CSS Modules。 |
在 <script setup> 中使用 this |
setup 內部沒有 this,會拋錯。 |
直接使用變數或函式名稱,或使用 ref.value 取得值。 |
| Props 變更直接修改 | Props 是只讀的,直接改會觸發 Vue 警告。 | 若需要本地化修改,使用 const local = ref(props.someProp) 或 computed 包裝。 |
| 樣式衝突 | 多個元件使用相同 class 名稱,scoped 無法解決全局樣式。 |
使用 BEM 命名法或 CSS Modules,確保 class 唯一性。 |
過度使用 v-if |
在同一層級大量 v-if 會導致頻繁的虛擬 DOM 重建。 |
優先使用 v-show(僅切換 display)或把條件抽離到子元件。 |
最佳實踐
- 統一使用
<script setup>:除非需要特殊的 Options API,否則建議全專案採用此語法,減少樣板程式碼。 - 將大型元件拆分為子 SFC:保持每個
.vue檔案的行數在 200 行以下,易於閱讀與測試。 - 使用 TypeScript:即使是小型專案,也能在開發階段捕捉錯誤,提升程式碼品質。
- 為每個元件寫測試:Vue Test Utils 搭配 Jest 可以對單檔元件的 props、emit、渲染結果進行單元測試。
- 遵循命名規範:檔案名稱使用 PascalCase(
MyButton.vue),組件名稱同樣使用 PascalCase,方便自動匯入。
實際應用場景
1. 表單元件庫
在企業內部開發 表單元件庫 時,每個表單欄位(如 TextInput.vue、DatePicker.vue)都可以是獨立的 SFC,透過 props 接收驗證規則,emit 回傳值變更。使用 <script setup> 可讓每個元件只保留必要的邏輯,減少重複程式碼。
2. 動態儀表板
儀表板往往需要根據使用者權限或資料量載入不同圖表。利用 動態匯入(defineAsyncComponent)與 Suspense,可以在使用者切換頁籤時,僅載入當前需要的圖表元件,降低首屏資源消耗。
3. 多語系與主題切換
將多語系文字與主題樣式抽離成獨立的 SFC(如 LocaleProvider.vue、ThemeSwitcher.vue),配合 provide / inject 或 Pinia 狀態管理,讓整個應用在切換語系或主題時,只需要更新少數幾個元件,即可即時反映全局變化。
總結
Vue 3 的 單檔元件(.vue) 為前端開發提供了一個高度整合、易於維護的開發模式。透過 <template>、<script setup>、<style scoped>(或 CSS Modules)的協同工作,我們可以:
- 快速建立 具備完整 UI、行為與樣式的元件。
- 利用 TypeScript 提升型別安全,減少執行期錯誤。
- 使用動態匯入 與 Suspense,優化大型應用的載入效能。
- 遵循最佳實踐(如
scoped、BEM、模組化 CSS)來避免常見的樣式與資料流問題。
熟練 SFC 的寫法,不僅能讓你在小型專案中快速迭代,也能在企業級應用中保持代碼的一致性與可測試性。現在就把上述概念套用到你的下一個 Vue 3 專案中,體驗「一檔搞定」的開發快感吧!祝開發順利 🎉