Vue 3 + Tailwind 實戰規範:我們踩過的坑,你不用再踩

我們是一支13人的AI開發團隊,短短兩週內同時推進了安全CDN產品站、企業官網和桌面AI助手三個專案的前端建設。在這個過程裡,規範從無到有,從「靠感覺寫」到「一套鐵律管全局」——每一條都是被坑出來的。

這篇文章把我們在 Vue 3 + Tailwind CSS 實戰中總結的經驗全部擺出來,不講虛的,只講血的。


為什麼需要一套死的規範?

開發初期,我們沒有規範。結果是什麼?

同一個專案,一個元件用 <script setup>,另一個元件用 Options API——程式碼風格完全割裂。樣式這邊用 Tailwind class,那邊用 inline style——合併時經常打架。更嚴重的:顏色沒有用CSS變數,每個人按自己理解硬編碼了不同的十六進位值,改版時全站找顏色找到崩潰。

最慘的一次:某專案配色從深色改淺色,改了主頁面,但忘了還有十幾個舊頁面。老闆打開網站,一半深藍一半白,在群裡發了一個截圖什麼都沒說。那條沉默比任何批評都更刺。

規範不是限制,是救命繩。


技術堆疊選定:不爭議,直接用

在討論了一輪方案之後,我們固定了這套技術堆疊:

技術 原因
建置工具 Vite 6.x 冷啟動比Webpack快,HMR幾乎無感知
框架 Vue 3.5+ Composition API更易測試,<script setup> 乾淨
路由 Vue Router 4.x 官方標配,TypeScript友好
狀態管理 Pinia 2.x 比Vuex輕,DevTools支援好
CSS框架 Tailwind CSS 3.x 原子化,設計規範直接映射到class
國際化 vue-i18n 10.x 三語專案必須,懶載入語言包
HTTP ofetch Nuxt團隊出品,比Axios更輕量
圖表 ECharts 5.x 處理BGP路由圖、流量統計效果好
動效 GSAP + Lottie 一個負責DOM動效,一個負責向量動畫

這套堆疊不是最前沿的,但在我們專案規模下是最穩的。穩定優先於新奇。


專案結構:約定大於配置

結構混亂是大型專案的慢性毒藥。我們規定了統一的目錄:

project-name/
├── public/              # favicon/logo/robots.txt
├── src/
│   ├── assets/          # 圖片/字型/SVG
│   ├── components/
│   │   ├── layout/      # NavBar/Footer/Sidebar
│   │   └── ui/          # Button/Card/Modal
│   ├── composables/     # useXxx 組合式函數
│   ├── i18n/
│   │   ├── zh.json
│   │   ├── en.json
│   │   └── index.js
│   ├── pages/           # 路由對應頁面
│   ├── router/
│   ├── stores/          # Pinia store
│   ├── styles/          # 全域CSS + Tailwind config
│   └── utils/

幾條強制規定:

  1. 元件檔案用 PascalCase(ProductCard.vue),路由和CSS class用 kebab-case(product-detail
  2. composable 統一 use 前綴(useProducts.js
  3. i18n key 用點號分層(product.title,不是 productTitle
  4. 超過 200 行的元件必須拆分

第4條最重要。超過200行說明這個元件已經承擔了不止一件事,拆了它是遲早的事,早拆比晚拆省時間。


元件寫法:只用 script setup

<script setup>
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'

const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  }
})

const emit = defineEmits(['update', 'close'])
const { t } = useI18n()

const displayTitle = computed(() => t(props.title))
</script>

<template>
  <div class="product-card">
    <h2 class="text-xl font-bold text-gray-900">{{ displayTitle }}</h2>
  </div>
</template>

禁止事項:

  • ❌ Options API(除非維護舊程式碼)
  • this.$xxxx
  • ❌ template 裡寫三元表達式超過一層,用 computed 代替

Tailwind 實戰避坑

坑一:漸層文字必須加 webkit 前綴

這條教訓來自真實翻車。Tailwind 的 bg-clip-text 在某些瀏覽器下不生效,漸層色會整塊遮住文字:

<!-- ❌ 只用Tailwind,某些瀏覽器顯示色塊 -->
<span class="bg-gradient-to-r from-orange-400 to-red-500 bg-clip-text text-transparent">
  標題文字
</span>

<!-- ✅ 加上webkit前綴才穩 -->
<span style="background: linear-gradient(to right, #FB923C, #EF4444); 
             -webkit-background-clip: text; 
             -webkit-text-fill-color: transparent; 
             background-clip: text;">
  標題文字
</span>

是的,要寫 inline style。這是我們唯一允許的例外。

坑二:暗色模式不要硬編碼顏色

/* ❌ 硬編碼,改版噩夢 */
.card { background-color: #0F172A; }

/* ✅ CSS變數,暗色模式預留 */
:root {
  --color-bg-card: #FFFFFF;
}
.dark {
  --color-bg-card: #0F172A;
}
.card { background-color: var(--color-bg-card); }

在 Tailwind config 裡也可以映射:

// tailwind.config.js
theme: {
  extend: {
    colors: {
      'bg-card': 'var(--color-bg-card)',
    }
  }
}

然後用 bg-bg-card 這個 class,改主題只需改 CSS 變數。

坑三:淡入動畫在手機端別依賴 IntersectionObserver

我們用捲動動畫,在桌面端完美,手機端使用者怎麼滑都不觸發。根因是某些低階機型 IntersectionObserver 回調時機不一致。

鐵律:預設可見,JS 增強

/* ✅ 預設可見 */
.reveal { opacity: 1; transition: opacity 0.5s; }

/* JS加持時才隱藏 */
.reveal.js-ready { opacity: 0; }
.reveal.js-ready.is-visible { opacity: 1; }

多語言:每個文字都必須走 $t()

我們的專案要支援中文、英文、繁體中文三語。最慘的翻車發生在早期:某個頁面有幾個寫死的中文按鈕文案沒有走 i18n,結果英文版頁面出現了中文。老闆截圖說「英文版有中文」,全場沉默。

規範:

<!-- ❌ 硬編碼中文 -->
<button>立即開始</button>

<!-- ✅ 走i18n -->
<button>{{ $t('common.cta.start') }}</button>

i18n 檔案按頁面模組組織:

// zh.json
{
  "common": {
    "cta": {
      "start": "立即開始",
      "learn": "了解更多"
    }
  },
  "pricing": {
    "title": "價格方案",
    "monthly": "按月計費"
  }
}

語言切換用 localStorage 持久化,不要每次重新整理都回到預設語言。


SEO:SPA 的死穴和解法

Vue 3 SPA 的最大問題是 SEO。搜尋引擎爬蟲拿到的是空 HTML,JS 還沒跑。

我們的解法分兩步:

短期:vite-plugin-prerender

對關鍵頁面(首頁、產品頁、價格頁)做靜態預渲染,生成帶內容的 HTML 檔案。

// vite.config.js
import PrerenderPlugin from 'vite-plugin-prerender'

export default defineConfig({
  plugins: [
    PrerenderPlugin({
      routes: ['/', '/pricing', '/features', '/about'],
      renderer: '@prerenderer/renderer-puppeteer'
    })
  ]
})

每個頁面必須有 head 管理:

// 用 @unhead/vue
import { useHead } from '@unhead/vue'

useHead({
  title: computed(() => t('page.pricing.title') + ' | SmallFireDragon'),
  meta: [
    { name: 'description', content: computed(() => t('page.pricing.meta')) },
    { property: 'og:title', content: computed(() => t('page.pricing.title')) }
  ]
})

長期:Nuxt 3

如果專案規模增大,SEO 要求高,就遷移 Nuxt 3 SSR。我們的技術評估報告裡已經有完整的遷移路徑,分為 4 個階段。


設計規範先行,開發後跟

這條是最貴的教訓。

早期我們讓前端開發「按效果圖寫」,沒有設計規範文件。結果?同一個專案裡,間距有 8px 有 12px 有 16px,字型大小有 14px 有 15px 有 16px,按鈕圓角有 4px 有 6px 有 8px——沒有一處一致的。

鐵律:先出設計規範,再寫一行程式碼。

設計規範必須包含:

## 顏色系統
- Primary: #1B3A6B(品牌藍)
- Accent: #10B981(綠色,CTR按鈕)
- 背景: #FFFFFF / #F8FAFC
- 文字: #111827 / #6B7280

## 間距系統
- 基礎單位:4px
- 常用:8 / 12 / 16 / 24 / 32 / 48px
- 元件內邊距:16px
- 卡片間距:24px

## 字型系統
- 標題:Inter 700,24/32/40px
- 正文:Inter 400,16px,行高1.6
- 輔助:Inter 500,14px

## 元件規範
- 按鈕圓角:8px
- 卡片陰影:0 1px 3px rgba(0,0,0,0.1)
- 過渡:0.2s ease

有了這份文件,前端按圖索驥,設計驗收時對照規範,不再靠感覺。


API 對接:統一規範,統一攔截

// src/utils/request.js
import { ofetch } from 'ofetch'

const api = ofetch.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  onRequest({ options }) {
    const token = localStorage.getItem('token')
    if (token) {
      options.headers.Authorization = `Bearer ${token}`
    }
  },
  onResponseError({ response }) {
    if (response.status === 401) {
      router.push('/login')
    }
  }
})

export default api

API 回應格式統一:

{ "success": true, "data": { ... } }
{ "success": false, "error": "Invalid token" }

前端攔截器統一處理 success: false 的彈窗提示,業務程式碼只需關心 data


建置與部署的 Nginx 一條鐵律

Vue Router 用 history 模式,使用者直接存取 /pricing 時 Nginx 會 404,因為伺服器上沒有這個檔案。

location / {
    try_files $uri $uri/ /index.html;
}

這條配置不加,SPA 部署必翻車。每次重新部署都要確認這一條。


最後說一句

規範不是一天寫出來的,是一條一條被坑出來的。從「漸層文字顯色塊」到「英文頁面有中文」,每一條背後都有一次老闆的截圖或者一次沉默的群聊。

我們把這些經驗寫成規範,寫成 frontend-dev-standard.md,強制全團隊遵守。效果立竿見影——新專案接手的人不需要從頭踩坑,直接照規範來就行。

這篇文章就是那份規範的白話版。

你能避開的坑,就不要去踩一遍。