本文 AI 產出,尚未審核

Vue3 – 樣式與 CSS 管理

主題:CSS Module


簡介

在大型前端專案中,樣式衝突是最常見的痛點之一。傳統的全域 CSS 容易因為命名不當或檔案載入順序而產生意外的樣式覆寫,讓維護成本急速上升。Vue3 提供的 CSS Module 機制,能自動將每個組件的樣式限制在該組件的作用域內,讓開發者可以以 模組化可預測 的方式管理 CSS。

本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,完整介紹在 Vue3 中如何使用 CSS Module,協助你在真實專案中快速上手、降低樣式衝突的風險。


核心概念

1. CSS Module 是什麼?

CSS Module 本質上是一套 編譯時 的 CSS 處理規則。當 Vue 的單檔組件(.vue)中使用 <style module> 時,編譯器會:

  1. 為每個 CSS 選擇器產生一個 唯一的 hash 名稱(如 button_1a2b3c)。
  2. 把這個 hash 注入到組件的 $style 物件中,供模板使用。
  3. 只在此組件渲染的 DOM 上套用這些經過 hash 的 class,避免全域污染。

優點

  • 完全避免樣式衝突
  • 支援局部變數、@import@keyframes 等完整 CSS 功能
  • 與 Vue 的 <script setup>、Composition API 完美結合

2. 在 Vue3 中啟用 CSS Module

在 Vue CLI、Vite 或 Nuxt 等工具鏈中,預設已支援 CSS Module,只要在 <style> 標籤加上 module 屬性即可。

<!-- MyButton.vue -->
<template>
  <button :class="$style.btn">{{ label }}</button>
</template>

<script setup>
defineProps({ label: String })
</script>

<style module>
.btn {
  padding: 0.5rem 1rem;
  background-color: #42b983;
  color: #fff;
  border: none;
  border-radius: 4px;
}
</style>

注意$style 只在 <template> 中可直接使用;在 <script> 中若需要動態取得 class,可透過 importuseCssModule()

3. 取得 $style 的方式

方式 使用情境 範例
直接在模板 ($style) 靜態 class 綁定 :class="$style.container"
useCssModule() (Composition API) <script setup> 或普通 <script> 中使用 const styles = useCssModule();
import styles from './Component.module.css' (Vite) 使用外部 CSS Module 檔案 import styles from './button.module.css'

範例:在 <script setup> 中使用 useCssModule

<template>
  <div :class="styles.wrapper">
    <p :class="styles.text">Hello CSS Module!</p>
  </div>
</template>

<script setup>
import { useCssModule } from 'vue'

const styles = useCssModule()
</script>

<style module>
.wrapper {
  padding: 1rem;
  background: #f5f5f5;
}
.text {
  color: #333;
  font-weight: bold;
}
</style>

4. 多個 CSS Module 及自訂名稱

如果一個組件需要 多個 CSS Module(例如同時使用 UI library 與自訂樣式),可以給 module 加上自訂名稱:

<style module="ui">
/* UI library 的樣式 */
.button {
  /* ... */
}
</style>

<style module="local">
/* 自己的樣式 */
.title {
  /* ... */
}
</style>

<template>
  <button :class="[ui.button, local.title]">混合樣式</button>
</template>

這樣在模板中就會產生兩個物件 uilocal,分別對應不同的 CSS Module。

5. 實用範例

以下提供 四個實務範例,示範不同情境下的 CSS Module 用法。每個範例都附上說明與重點提示。

範例 1:基本的 Button 組件

<!-- src/components/BaseButton.vue -->
<template>
  <button :class="$style.btn" @click="$emit('click')">
    <slot>按鈕</slot>
  </button>
</template>

<script setup>
defineEmits(['click'])
</script>

<style module>
.btn {
  display: inline-block;
  padding: 0.6rem 1.2rem;
  background: #42b983;
  color: #fff;
  border-radius: 4px;
  cursor: pointer;
  transition: opacity 0.2s;
}
.btn:hover {
  opacity: 0.9;
}
</style>

重點:使用 $style.btn 直接綁定,保證此按鈕的樣式不會被其他全域 .btn 影響。

範例 2:結合外部 CSS Module 檔案

/* src/assets/styles/card.module.scss */
.card {
  border: 1px solid #e0e0e0;
  border-radius: 6px;
  padding: 1rem;
  background: #fff;
}
.title {
  font-size: 1.25rem;
  margin-bottom: 0.5rem;
}
<!-- src/components/Card.vue -->
<template>
  <div :class="styles.card">
    <h3 :class="styles.title"><slot name="title"/></h3>
    <div><slot/></div>
  </div>
</template>

<script setup>
import styles from '@/assets/styles/card.module.scss'
</script>

說明:Vite 直接支援 .module.scss,只要 import 即可取得 hash 後的 class 名稱。

範例 3:使用 useCssModule 動態切換樣式

<template>
  <div :class="currentClass">切換顏色</div>
  <button @click="toggle">切換</button>
</template>

<script setup>
import { ref } from 'vue'
import { useCssModule } from 'vue'

const styles = useCssModule()
const isRed = ref(true)

const currentClass = computed(() => (isRed.value ? styles.red : styles.blue))

function toggle() {
  isRed.value = !isRed.value
}
</script>

<style module>
.red {
  color: #e53935;
}
.blue {
  color: #1e88e5;
}
</style>

技巧computed 搭配 $style(或 styles)可在執行時切換不同的樣式,仍保持模組化的安全性。

範例 4:多個 Module 與自訂名稱

<template>
  <nav :class="[layout.nav, theme.nav]">
    <a :class="layout.link" href="#">首頁</a>
    <a :class="layout.link" href="#">關於</a>
  </nav>
</template>

<script setup>
import { useCssModule } from 'vue'

const layout = useCssModule('layout')
const theme = useCssModule('theme')
</script>

<style module="layout">
.nav {
  display: flex;
  gap: 1rem;
}
.link {
  text-decoration: none;
  color: inherit;
}
</style>

<style module="theme">
.nav {
  background: #212121;
  padding: 0.5rem;
}
</style>

重點:同名的 CSS class(如 .nav)在不同 Module 中會產生不同的 hash,互不干擾,讓 UI 結構與主題樣式可以分離管理。


常見陷阱與最佳實踐

陷阱 可能原因 解決方式 / 最佳實踐
樣式找不到 (undefined is not an object (reading '$style')) <style> 沒加 module 或檔案命名錯誤 確認 <style module><style module="name"> 正確寫入;若使用外部 .module.css,檔名必須包含 .module.
全域 CSS 仍然影響 使用了 深度選擇子(如 >>>/deep/)或 ::v-deep 盡量避免深度選擇子;若必須,改用 CSS Module + BEM 的命名方式,或把需要的全域樣式搬到另一個 Module
Hash 名稱衝突(在開發版看到相同的 class) 懶惰載入(dynamic import)導致同一檔案被多次編譯 使用 vite-plugin-css-modulesvue-cli 的預設配置,保證每個檔案只被編譯一次
@keyframes 無效 keyframes 名稱未被 hash,導致全域衝突 在 Module 中使用 :global 標記或直接在 <style>(非 module)裡定義 keyframes,再在 Module 中引用
在 TypeScript 中取得 $style TypeScript 無法推斷 $style 型別 shims-vue.d.ts 中加入 declare module '*.module.css' { const classes: Record<string, string>; export default classes; } 以及 declare module '*.module.scss' { ... }

最佳實踐

  1. 盡量讓每個組件只保有一個 <style module>,避免混用全域與模組樣式。
  2. 使用 BEM 命名法結合 CSS Module(如 button__icon),即使在編譯後變成 button__icon_1a2b3c,可提升可讀性。
  3. 把共用變數、mixins、reset 放在全域樣式src/assets/styles/global.css),其餘皆走 Module。
  4. 在 Vite 中啟用 css.modules 設定,可自訂產生的 hash 格式(如 [name]__[local]___[hash:base64:5]),方便除錯。
  5. 測試時檢查產生的 class:開發者工具中會看到 button_1a2b3c,若看到未 hash 的名稱,代表該樣式未被 Module 包裹。

實際應用場景

場景 為何選擇 CSS Module
大型企業內部系統 多個開發團隊同時維護不同功能模組,避免樣式相互覆寫。
設計系統(Design System) 每個 UI 元件(Button、Card、Modal)都有獨立的樣式模組,方便版本管理與發佈。
多主題切換(Dark / Light) 主題樣式放在一個獨立的 Module,透過動態切換 theme Module 內的 class,保持組件本身的樣式不變。
SSR(Server‑Side Rendering) CSS Module 在編譯階段就產生唯一 class,SSR 時不會因為樣式先後順序產生 FOUC(Flash of Unstyled Content)。
Vue3 + Vite + TypeScript Vite 原生支援 .module.css/.module.scss,配合 TypeScript 的型別聲明,可在 IDE 中即時提示可用的 class 名稱。

案例簡述:某電商平台的商品卡片(ProductCard.vue)使用 CSS Module 管理卡片的排版、陰影與按鈕樣式,並在 ThemeSwitcher.vue 中動態載入 dark.module.csslight.module.css,完成即時主題切換,且不會因為其他開發者新增全域 .card 樣式而破壞原有排版。


總結

  • CSS Module 為 Vue3 提供了 模組化、唯一性的樣式管理,有效解決全域 CSS 的衝突問題。
  • 只要在 <style> 標籤加上 module,或使用 .module.css/.module.scss 檔案,即可自動取得 $style(或 useCssModule())物件。
  • 透過 多個 Module、命名自訂、useCssModule 等技巧,可在同一組件內同時管理結構樣式、主題樣式與第三方 UI 樣式。
  • 常見的陷阱包括忘記加 module、深度選擇子造成全域污染、@keyframes 未被 hash 等,依照本文提供的解決方案即可快速排除。
  • 在實務專案中,將 共用變數/全域重置 放在全域樣式,將 元件專屬樣式 放入 CSS Module,配合 BEM 命名自訂 hash 格式,即可建立易維護、可擴充的樣式架構。

掌握 CSS Module 後,你的 Vue3 專案將能在 樣式安全性開發效率可維護性 上得到顯著提升,從此不再為樣式衝突而頭痛。祝開發順利!