---
# System prepended metadata

title: "\U0001F9F0 Vue + Vite 專案初始化流程指南"
tags: [環境相關]

---

# 🧰 Vue + Vite 專案初始化流程指南

這份筆記將完整記錄如何從零開始建立一個 Vue 3 + Vite 的前端專案，並搭配常用開發工具、UI 框架與專案結構設計，讓你在未來可以快速展開開發、除錯與維護工作。

## 🧱 建立環境基礎：專案初始設置

在開始撰寫 Vue 專案之前，我們要先建立好一個能順利執行、順利開發的「開發環境」。這包括使用 Vite 建立 Vue 專案骨架，安裝 Vue 工具函式庫與開發用插件，並初始化 Node.js 的開發環境。

### 🪜 Step 1：建立 Vue + Vite 專案

首先，透過 Vite 提供的快速初始化指令建立新專案：

```bash
npm create vite@latest
```

依照指示選擇：

```
✔ Project name: » vite-project（←你想要的專案名稱）
✔ Select a framework: » Vue
✔ Select a variant: » JavaScript
```

接著進入專案資料夾並安裝套件：

```bash
cd vite-project
npm install
```

### 🔌 安裝 Vue 工具函式庫與開發插件

這裡我們加入一些輔助開發的工具，提升開發效率與可觀察性：

```bash
npm i @vueuse/core
npm i -D vite-plugin-inspect
```

📌 用途說明：

* `@vueuse/core`：提供大量 Vue Composition API 的實用函式，例如 `useMouse()`（監聽滑鼠位置）、`useDark()`（切換暗色模式）等。
* `vite-plugin-inspect`：可用於檢查目前 Vite 專案中已掛載的插件與設定情況，適合除錯與優化。

---

## 🧩 建立後端開發基礎與環境變數設定

雖然我們的專案主軸是 Vue 前端，但為了日後串接 API、模擬資料流程或整合 Express 等 Node.js 工具，我們也要為 Node 環境做基本設置。

### 🛠 Step 2：初始化 Node 專案

```bash
npm init --yes
npm i -D nodemon
```

📌 `nodemon` 是一個開發用工具，可以監聽檔案變動，自動重啟後端伺服器，避免每次修改都要手動重開。

---

## 🚦 安裝 Vue Router（前端路由工具）

頁面跳轉是 SPA 應用的核心，這裡我們安裝 Vue 官方的路由套件：

```bash
npm i vue-router
```

並建立路由設定檔與首頁頁面：

```
src/router/index.js
src/views/HomeView.vue
```

```js
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const routes = [{ path: '/', component: HomeView }]

export default createRouter({
  history: createWebHistory(),
  routes
})
```

---

## 🌐 建立 Express 後端伺服器

如果你需要處理 API 或串接資料庫，Express 是最常用的 Node.js 後端框架。我們同時也需要用 `dotenv` 管理機密參數：

```bash
npm i express dotenv
```

📌 套件說明：

* `express`：快速建立後端伺服器、API 接口、處理 HTTP 請求。
* `dotenv`：讀取 `.env` 檔案中的環境變數，避免敏感資訊硬寫在程式碼中。

---

## 🔒 建立 `.env` 與 `.gitignore` 檔案

接下來建立兩個重要的開發相關設定檔：

### 📝 `.env`

```bash
# 檔名
.env
```

這個檔案通常會包含如下資訊：

```
PORT=3000
VITE_API_BASE_URL=http://localhost:3000
```

### 📝 `.gitignore`

```bash
# 檔名
.gitignore
```

基本內容如下（告訴 Git 忽略某些檔案與資料夾）：

```
.env
node_modules
dump/*
!dump/.gitkeep
```

其中 `.gitkeep` 是一個技巧，用來保留空資料夾（因 Git 預設不會追蹤空資料夾）。

---

## ✨ 開發環境檢查工具：ESLint + Prettier 安裝與設定

一個乾淨的程式碼庫不只幫助你自己維護，也讓團隊合作更順利。我們透過 ESLint 搭配 Prettier 做語法檢查與格式統一：

### 🧪 安裝套件

```bash
npm init @eslint/config@latest
npm i -D eslint prettier eslint-plugin-prettier eslint-config-prettier eslint-plugin-vue
```

📌 工具說明：

| 套件名稱                     | 用途                                 |
| ------------------------ | ---------------------------------- |
| `eslint`                 | 語法檢查工具，找出潛在錯誤與風格問題                 |
| `prettier`               | 自動格式化工具，讓程式碼有統一的風格                 |
| `eslint-plugin-prettier` | 將 Prettier 納入 ESLint 檢查流程中         |
| `eslint-config-prettier` | 關閉 ESLint 中與 Prettier 重疊的格式規則，避免衝突 |
| `eslint-plugin-vue`      | 專門針對 `.vue` 檔案進行檢查的 ESLint 插件      |

---

### 📄 設定檔：eslint.config.js

建立一個設定檔 `eslint.config.js`，讓 ESLint 能依照我們的需求來運作：

```js
import js from '@eslint/js'
import globals from 'globals'
import { defineConfig } from 'eslint/config'
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'

export default defineConfig([
  {
    files: ['**/*.{js,mjs,cjs}'],
    plugins: { js },
    extends: ['js/recommended'],
  },
  { files: ['**/*.{js,mjs,cjs}'], languageOptions: { globals: globals.node } },
  eslintPluginPrettierRecommended,
  {
    rules: {
      'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
    },
  },
])
```

---

### 📄 設定檔：.prettierrc.json

接著建立 Prettier 的設定檔 `.prettierrc.json`：

```json
{
  "$schema": "https://json.schemastore.org/prettierrc",
  "printWidth": 100,
  "semi": false,
  "singleQuote": true
}
```

這份設定會讓你的程式碼使用單引號、句尾不加分號，並控制每行最多寬度為 100 字元。

---

## 🎨 安裝 UI 框架：PrimeVue（含配套工具）

如果你選擇使用 PrimeVue 作為 UI 元件庫，可以一次安裝所需的所有資源：

```bash
npm i primevue@latest primeicons primeflex
```

| 套件名稱         | 用途說明                                   |
| ------------ | -------------------------------------- |
| `primevue`   | 核心 UI 元件庫                              |
| `primeicons` | 圖示套件，搭配 `<i class="pi pi-check" />` 使用 |
| `primeflex`  | CSS 工具套件（類似 Tailwind）                  |

---

## 🌟 PrimeVue 整合流程（main.js 範例）

安裝完 PrimeVue 套件後，接下來的關鍵就是在 `main.js` 中完成設定，讓整個 Vue 專案能正確使用 PrimeVue 的元件、樣式與圖示。

### 📥 匯入 PrimeVue 所需資源

```js
import { createApp } from 'vue'
import App from './App.vue'

// PrimeVue 核心 & 樣式
import PrimeVue from 'primevue/config'
import 'primevue/resources/themes/lara-light-blue/theme.css' // 🎨 主題樣式
import 'primevue/resources/primevue.min.css'                 // 📦 元件基礎樣式
import 'primeicons/primeicons.css'                           // 🔠 圖示庫
import 'primeflex/primeflex.css'                             // 🧱 CSS 工具套件
```

📌 每一個匯入檔案都有它的用途：

| 匯入內容                              | 用途說明                                                  |
| --------------------------------- | ----------------------------------------------------- |
| `PrimeVue from 'primevue/config'` | 主套件設定入口，需搭配 `app.use(PrimeVue)` 使用                    |
| `theme.css`（主題樣式）                 | 定義元件整體風格（顏色、圓角、陰影等）                                   |
| `primevue.min.css`（元件基本樣式）        | 所有元件的基礎樣式，沒有它元件會沒外型                                   |
| `primeicons.css`（圖示）              | 使用 `<i class="pi pi-check" />` 的前提                    |
| `primeflex.css`（排版工具）             | 類似 Tailwind，支援 `.flex`, `.justify-content-center` 等類別 |

---

### 🚀 啟動 Vue App 並載入 PrimeVue

```js
const app = createApp(App)
app.use(PrimeVue)
app.mount('#app')
```

這段程式碼執行以下操作：

| 動作               | 說明                                           |
| ---------------- | -------------------------------------------- |
| `createApp(App)` | 建立整個 Vue 應用程式                                |
| `.use(PrimeVue)` | 啟用 PrimeVue，讓其內建元件與樣式生效                      |
| `.mount('#app')` | 把這整個應用程式掛載到 `index.html` 中的 `<div id="app">` |

---

## 🔧 元件註冊方式：局部 vs 全域

PrimeVue 元件不是自動可用，你必須選擇以下兩種註冊策略：

### 1️⃣ 個別引入（效率較佳）

```js
import Button from 'primevue/button'
app.component('Button', Button)
```

這樣在任何 `.vue` 檔中就能直接使用：

```vue
<Button label="點我" />
```

### 2️⃣ 全域集中管理（開發較快）

你也可以在 `src/plugins/primevue.js` 裡統一註冊：

```js
// plugins/primevue.js
import Button from 'primevue/button'

export default (app) => {
  app.component('Button', Button)
}
```

然後在 `main.js` 中：

```js
import registerPrimeVue from './plugins/primevue'
registerPrimeVue(app)
```

這種寫法讓元件註冊更集中，也方便日後維護。

---

## 🧩 PrimeVue + 元件使用小提醒

| 類型       | 說明                                                           |
| -------- | ------------------------------------------------------------ |
| UI 樣式    | 每個元件的風格依賴 `theme.css`，主題可隨時更換                                |
| Icon 使用  | 所有圖示都來自 `primeicons`，格式為 `<i class="pi pi-xxx"></i>`         |
| CSS 工具套件 | `primeflex` 提供 `.flex`, `.grid`, `.align-items-center` 等排版類別 |

---

## 🧰 提升開發效率的懶人神器們：自動化插件介紹

當專案越來越大、元件與路由越來越多時，我們會發現很多事情很「重複」又「容易忘記」，例如：

* 每次都要自己 import `ref`、`computed`
* 每新增一個元件都要手動 `import` 再 `components: {}` 註冊
* 每次新增 `.vue` 頁面都要自己手寫 route 設定
* 不同頁面需要不同版型（layout）又不想一直用 `<template v-if>` 切換

這時候你就會愛上這四大工具組合：

### 🔧 安裝一次全搞定

```bash
npm i -D unplugin-auto-import unplugin-vue-components unplugin-vue-router vite-plugin-vue-layouts-next unplugin-fonts
```

---

## 1️⃣ `unplugin-auto-import`：自動引入常用函式

你不再需要手動寫這些：

```js
import { ref, computed } from 'vue'
```

直接寫：

```js
const count = ref(0)
```

就能用！它會自動幫你引入。

### ✅ Vite 設定範例

```js
AutoImport({
  imports: [
    'vue',
    'vue-router',
    {
      pinia: ['defineStore', 'storeToRefs'],
    },
  ],
  vueTemplate: true,
  eslintrc: {
    enabled: true,
  },
})
```

---

## 2️⃣ `unplugin-vue-components`：自動註冊元件

你不必再手動這樣做：

```js
import MyButton from '@/components/MyButton.vue'
```

也不用加進 `components: {}`，只要 `.vue` 檔案放在指定資料夾，這個插件就會幫你註冊好！

### ✅ 設定範例（含 PrimeVue 元件支援）

```js
import Components from 'unplugin-vue-components/vite'
import { PrimeVueResolver } from 'unplugin-vue-components/resolvers'

Components({
  resolvers: [PrimeVueResolver()],
  dts: 'src/components.d.ts',
})
```

---

## 3️⃣ `unplugin-vue-router`：自動建立路由表

這是新一代的 Vue 路由管理工具，會自動掃描 `/src/pages` 資料夾下的檔案，幫你產生對應的路由設定。

| 檔案結構                      | 自動對應路由      |
| ------------------------- | ----------- |
| `src/pages/index.vue`     | `/`         |
| `src/pages/about.vue`     | `/about`    |
| `src/pages/blog/[id].vue` | `/blog/:id` |

### ✅ 基本設定

```js
VueRouter({
  routesFolder: 'src/pages',
  dts: 'src/typed-router.d.ts',
})
```

### ✅ main.js 使用方式

```js
import { createRouter, createWebHistory } from 'vue-router/auto'
const router = createRouter({ history: createWebHistory() })
```

---

## 4️⃣ `vite-plugin-vue-layouts-next`：自動套用頁面版型

如果你有設計多個 layout（例如登入頁 vs 後台頁 vs 公開頁），這個插件讓每個頁面自動套用對應 layout，不用在 `<template>` 裡加一堆 `v-if` 判斷。

### ✅ 基本用法

1. 在 `/src/layouts/` 裡建立：

   * `default.vue`
   * `admin.vue`
   * `auth.vue`

2. 在頁面元件中指定 layout：

```vue
<script setup>
definePageMeta({
  layout: 'admin'
})
</script>
```

3. 搭配 `unplugin-vue-router` 使用，讓路由自動延伸套 layout！

---

## 5️⃣ `unplugin-fonts`：自動載入 Google Fonts 字型資源

在開發 Vue 專案時，若需要使用 Google Fonts，傳統做法是手動複製 `<link>` 到 `index.html`，不僅麻煩且難以管理樣式權重。
`unplugin-fonts` 是一個專為 Vite 打造的開發工具，能夠協助你**自動引入 Google Fonts 並注入樣式**，適合開發階段快速套用與切換字型。

### ⚠️ 使用注意事項

| 項目            | 說明                                           |
| ------------- | -------------------------------------------- |
| 僅為開發輔助工具      | `unplugin-fonts` 適合快速套用字型於開發過程，不建議作為最終字型部署方案 |
| 正式環境建議使用最佳化方式 | 可考慮使用自架字型、subset 字型檔、或 CDN 搭配快取方式減少資源載入      |
| 僅載入字型，不自動指定樣式 | 套件只會幫你載入字型，並不會自動套用到網頁中的 `font-family` 屬性     |

### ✅ 基本用法

1. 在 Vite 設定範例

```js
import Fonts from 'unplugin-fonts/vite'

export default defineConfig({
  plugins: [
    Fonts({
      google: {
        families: [
          {
            name: 'Roboto',
            styles: 'wght@100;300;400;500;700;900',
          },
          {
            name: 'Noto Sans TC',
            styles: 'wght@400;700',
          },
        ],
      },
    }),
  ],
})
```

2.  字型樣式使用方式

字型被載入後，仍需自行在樣式中指定 `font-family`：

```css
body {
  font-family: 'Roboto', sans-serif;
}
```

---

## 🎯 插件們的搭配關係圖

| 目標            | 套件                             | 說明                                  |
| ------------- | ------------------------------ | ----------------------------------- |
| 自動引入函式        | `unplugin-auto-import`         | `ref`、`useRouter()`、`defineStore()` |
| 自動註冊元件        | `unplugin-vue-components`      | `MyButton.vue` 會自動變可用               |
| 自動產生路由        | `unplugin-vue-router`          | 根據 `/pages` 檔案自動產生路由                |
| 自動套用頁面 layout | `vite-plugin-vue-layouts-next` | 根據 `definePageMeta()` 自動掛載 layout   |
| 自動載入 Google 字型 | `unplugin-fonts`                | 自動產生 `<link>` 並載入指定 Google Fonts |


---

## ⚙️ `vite.config.js`：整合所有插件的設定範本

這裡是一份包含 Vue 插件、懶人工具、自動註冊與 proxy 設定的 `vite.config.js` 完整範例。這樣寫好一次就能支援：

* `.vue` 檔案的解析
* Vue Router 的自動路由功能
* PrimeVue 元件的自動註冊
* Layout 自動套用
* HTTP proxy 跨域設定
* 快速 alias `@` 對應到 `/src`

### ✅ vite.config.js 範例內容

```js
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { PrimeVueResolver } from 'unplugin-vue-components/resolvers'
import Fonts from 'unplugin-fonts/vite'
import Layouts from 'vite-plugin-vue-layouts-next'
import VueRouter from 'unplugin-vue-router/vite'
import { VueRouterAutoImports } from 'unplugin-vue-router'
import { fileURLToPath, URL } from 'node:url'

export default defineConfig({
  plugins: [
    VueRouter(),
    Layouts(),
    Vue(),

    Components({
      resolvers: [PrimeVueResolver()],
    }),

    Fonts({
      google: {
        families: [
          {
            name: 'Roboto',
            styles: 'wght@100;300;400;500;700;900',
          },
        ],
      },
    }),

    AutoImport({
      imports: [
        'vue',
        VueRouterAutoImports,
        {
          pinia: ['defineStore', 'storeToRefs'],
        },
      ],
      eslintrc: {
        enabled: true,
      },
      vueTemplate: true,
    }),
  ],
  optimizeDeps: {
    exclude: [
      'vuetify', // ← 若你沒用 Vuetify 可刪
      'vue-router',
      'unplugin-vue-router/runtime',
    ],
  },
  define: { 'process.env': {} },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('src', import.meta.url)),
    },
    extensions: ['.js', '.json', '.vue'],
  },
  server: {
    port: 3000,
    proxy: {
      '/api': 'http://localhost:3000', // ← 跨域處理（前端 → 後端）
    },
  },
  css: {
    preprocessorOptions: {
      sass: { api: 'modern-compiler' },
      scss: { api: 'modern-compiler' },
    },
  },
})
```

---

## 🗂 專案資料夾結構設計建議

有了這些插件後，我們也要讓 `src/` 下的資料夾分類清楚、可擴充。以下是實務上常見的結構：

```
src/
├── assets/         # 靜態資源（圖片、影片、樣式）
├── styles/         # SCSS / 全域樣式、變數、reset
├── components/     # 可重用 UI 元件（按鈕、Modal、表單欄位）
├── pages/          # 路由對應的頁面元件（搭配 unplugin-vue-router）
├── layouts/        # 各種畫面版型（登入頁、管理頁等）
├── router/         # Vue Router 設定（若沒用自動路由）
├── plugins/        # 插件註冊（如 PrimeVue, VeeValidate 等初始化）
├── composables/    # 自定義邏輯模組 useXXX()
├── store/          # 狀態管理區（使用 pinia）
├── services/       # axios 請求封裝、API 溝通邏輯
├── utils/          # 純 JavaScript 工具（轉格式、處理字串）
├── constants/      # 靜態資料、選單清單、表單選項
├── App.vue         # Vue 入口元件
└── main.js         # 啟動點（引入 UI 框架、Router、Store 等）
```

這份結構搭配你的套件安裝方式，是一份高擴充性的通用專案骨架。

---

## 🚀 Vue 應用程式的初始化主程式：main.js

你的 `main.js` 是 Vue 專案的「大腦啟動區」，用來：

1. 建立整個 App
2. 掛上 PrimeVue、Router、Pinia 等插件
3. 引入主題樣式與全域資源
4. 把 App 掛載到 HTML 裡的 `<div id="app">`

---

### ✨ `main.js` 典型範本：整合 PrimeVue + Pinia + Router

```js
import { createApp } from 'vue'
import App from './App.vue'

// 🔌 PrimeVue 套件與樣式
import PrimeVue from 'primevue/config'
import 'primevue/resources/themes/lara-light-blue/theme.css'
import 'primevue/resources/primevue.min.css'
import 'primeicons/primeicons.css'
import 'primeflex/primeflex.css'

// 🧠 狀態管理
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

// 🚦 Router（可選用手動或自動路由）
import router from './router' // 若你用 unplugin-vue-router 改成 import { createRouter }...

const app = createApp(App)

// 🧩 使用插件
app.use(PrimeVue)

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)

app.use(router)

app.mount('#app')
```

---

## 🔍 各部分補充說明

### 1️⃣ PrimeVue 的樣式與啟用

* `PrimeVue from 'primevue/config'`：註冊 PrimeVue 套件功能
* `'primevue/resources/themes/xxx/theme.css'`：指定主題風格（可切換）
* `'primevue/resources/primevue.min.css'`：元件基本樣式（必引入）
* `'primeicons/primeicons.css'`：圖示庫
* `'primeflex/primeflex.css'`：彈性排版 CSS 類別（像 Tailwind）

---

### 2️⃣ Pinia 狀態管理系統

```js
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
```

* `createPinia()`：建立 Pinia 實例
* `piniaPluginPersistedstate`：讓狀態可儲存至 localStorage（記住登入狀態等）

---

### 3️⃣ Router

如果你用的是傳統 router：

```js
import router from './router'
```

搭配路由定義檔 `src/router/index.js` 即可。

若你使用的是 `unplugin-vue-router` 自動路由：

```js
import { createRouter, createWebHistory } from 'vue-router/auto'
const router = createRouter({ history: createWebHistory() })
```

若有使用 `vite-plugin-vue-layouts-next`，記得加入：

```js
import { setupLayouts } from 'virtual:generated-layouts'
const router = createRouter({
  history: createWebHistory(),
  extendRoutes: setupLayouts, // ⬅ 讓路由能自動套用 layout
})
```

---

## 🎛 元件註冊策略補充（PrimeVue 範例）

除了在 `main.js` 中直接註冊元件（效率較佳），你也可以建立一個元件集中註冊檔，例如：

```js
// plugins/primevue.js
import Button from 'primevue/button'
import Dialog from 'primevue/dialog'
import InputText from 'primevue/inputtext'

export default (app) => {
  app.component('Button', Button)
  app.component('Dialog', Dialog)
  app.component('InputText', InputText)
}
```

然後在 `main.js` 引入：

```js
import registerPrimeVue from './plugins/primevue'
registerPrimeVue(app)
```

📦 好處：未來元件多了也只要改 plugins 檔，不用再動 main.js，結構清晰、開發快。

---

## 🔁 axios × Express × Proxy 整合指南

當你要讓前端（Vue）與後端（Express）互相溝通時，最核心的步驟就是透過 **axios 發送請求**，並透過 **proxy 解決開發時的跨域問題**。

---

## 🧩 建立 axios 請求流程

### 安裝 axios

```bash
npm i axios
```

### 建議建立專屬的 API 請求資料夾

```
src/services/userService.js
```

```js
// userService.js
import axios from 'axios'

const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000'

export const login = (formData) => axios.post(`${baseURL}/login`, formData)
export const fetchUser = (id) => axios.get(`${baseURL}/user/${id}`)
```

這樣的好處是：**集中管理所有 API 請求邏輯**，方便擴充與維護。

---

### 可加入錯誤處理版本

```js
export const fetchUser = async (id) => {
  try {
    const res = await axios.get(`${baseURL}/user/${id}`)
    return res.data
  } catch (error) {
    console.error('❌ 無法取得使用者資料', error)
    throw error
  }
}
```

---

## 🌐 .env 中設定 baseURL

開發時不建議把 URL 寫死在程式碼中，應該放在 `.env`：

```
VITE_API_BASE_URL=http://localhost:3000
```

然後在程式中這樣取用：

```js
const baseURL = import.meta.env.VITE_API_BASE_URL
```

⚠️ 注意：只有以 `VITE_` 開頭的變數，才會被前端讀到！

---

## 🧭 為什麼需要 Vite Proxy？

開發時，前端跑在：

```
http://localhost:5173
```

後端跑在：

```
http://localhost:3000
```

這是「不同源」，會造成 CORS 錯誤：

```txt
❌ Access to fetch at 'http://localhost:3000/api/xxx' has been blocked by CORS policy
```

✅ 解法：讓 Vite 幫你偷偷轉送請求

---

## 🛠 在 vite.config.js 設定 Proxy

```js
server: {
  proxy: {
    '/api': 'http://localhost:3000'
  }
}
```

然後前端就可以這樣寫 axios：

```js
axios.get('/api/hello') // ← 不用寫死 http://localhost:3000
```

📌 意思是：當你 axios 請求 `/api/xxx`，Vite 會自動幫你轉送到後端 `http://localhost:3000/api/xxx`

---

## 🧱 Express 後端 API 範例（含 .env）

建立 `server/index.js`：

```js
import express from 'express'
import dotenv from 'dotenv'
dotenv.config()

const app = express()
const port = process.env.PORT || 3000

app.get('/api/hello', (req, res) => {
  res.json({ message: 'Hello from backend!' })
})

app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`)
})
```

`.env` 設定：

```
PORT=3000
```

---

## 🎯 Vue 前後端請求實際流程圖

```
[ Vue App 啟動 ]
        ↓
[ axios 發送 /api/hello ]
        ↓
[ Vite Proxy 幫忙轉送 ]
        ↓
[ Express 接收到 /api/hello ]
        ↓
[ 回傳 JSON 給前端 ]
        ↓
[ Vue 拿到資料 → 顯示在畫面 ]
```

✅ 這就是典型的前後端串接流程。

---

## 🧩 Vue × Express 開發流程 × 檔案位置 × 執行順序

### 🔄 實務開發的建議順序

| 階段            | 動作說明                                      | 建議路徑                   |
| ------------- | ----------------------------------------- | ---------------------- |
| 1️⃣ 後端 API 建立 | 撰寫 `server/index.js`，確保可回傳基本資料            | `/server/index.js`     |
| 2️⃣ 加上 `.env` | 設定後端啟動 Port、API Key 等敏感資訊                 | `/.env`                |
| 3️⃣ 前端初始化     | 建立 Vite + Vue 專案，安裝套件與撰寫畫面元件              | `/client/`             |
| 4️⃣ 加入 proxy  | 設定 `vite.config.js` 中的 proxy，讓前端能接到後端 API | `/vite.config.js`      |
| 5️⃣ 設定 axios  | 建立 axios 封裝檔案，設定 baseURL 與錯誤處理邏輯          | `/src/services/xxx.js` |
| 6️⃣ 撰寫呼叫功能    | 在 Vue 頁面組件中使用 axios 呼叫 API 並渲染畫面          | `/src/pages/xxx.vue`   |

---

## 🌍 多環境支援：不同 baseURL 的處理方式

### .env（開發用）

```
VITE_API_BASE_URL=http://localhost:3000
```

### .env.production（部署用）

```
VITE_API_BASE_URL=https://api.yourdomain.com
```

### axios 使用方式

```js
const baseURL = import.meta.env.VITE_API_BASE_URL
axios.get(`${baseURL}/api/data`)
```

Vite 會根據執行命令，自動載入對應 `.env` 檔案：

* `vite` / `npm run dev` → 使用 `.env`
* `vite build` → 使用 `.env.production`

---

## 🛠 Proxy 補充設定（多環境寫法）

```js
const isDev = process.env.NODE_ENV === 'development'

export default defineConfig({
  server: {
    proxy: {
      '/api': isDev
        ? 'http://localhost:3000'
        : 'https://api.myapp.com',
    },
  },
})
```

---

## 🚨 常見錯誤對照表

| 錯誤訊息                                   | 原因                    | 解法或補充說明                             |
| -------------------------------------- | --------------------- | ----------------------------------- |
| `CORS blocked`                         | 前後端不同源 → 被瀏覽器擋下跨域     | 加 Vite proxy 或後端設定 CORS header      |
| `Cannot GET /api/...`                  | Express 未正確設路由        | 確認 `app.get()` 有對應、檔案確實被啟動          |
| `router-view 沒顯示畫面`                    | 路由設定錯誤 / router 未註冊   | 確認路由名稱拼寫正確，main.js 有 `.use(router)` |
| `.env 無效或讀不到`                          | 未加上 `VITE_` 開頭        | 前端 `.env` 中變數需為 `VITE_XXX` 格式       |
| `import.meta.env.VITE_XXX` 為 undefined | 變數名稱打錯 / `.env` 檔位置錯誤 | 檢查變數拼字與是否放在專案根目錄                    |

---

## 🧪 測試 API 是否串接成功

1. 後端：Express 中加上測試 API

```js
app.get('/api/test', (req, res) => {
  res.json({ status: 'ok' })
})
```

2. 前端：axios 發送請求

```js
axios.get('/api/test').then(res => {
  console.log(res.data) // 應該會看到 { status: 'ok' }
})
```

3. 啟動流程：

```
node server/index.js
npm run dev（Vite）
```

✅ 若成功，代表 proxy、後端、axios 全部串接順利！

---

## 📁 Vue 專案中的資料夾職責 × 實務慣例全解析

在 Vue 專案開發中，資料夾分類是維持「清晰結構、易於維護」的關鍵。這裡幫你整理每個資料夾的責任角色、適合放的內容與何時該建立。

---

### 🧱 頁面與元件層級

| 資料夾           | 📦 責任  | 🧠 說明與內容                                                |
| ------------- | ------ | ------------------------------------------------------- |
| `views/`      | 頁面元件層級 | 一個 `.vue` 對應一個「頁面」，通常對應 router 的某個 path，例如：首頁、登入頁、表單頁等。 |
| `components/` | 可重用元件  | 放「跨頁面可重複使用」的元件，例如按鈕、卡片、Modal、表單欄位等。小顆、模組化、支援 props。     |

✅ 小提醒：
`views/` 是「對外」畫面，`components/` 是「內部」積木。

---

### 🔁 應用支援工具區

| 資料夾                   | 📦 責任類型   | 🧠 用途與範例                                                             |
| --------------------- | --------- | -------------------------------------------------------------------- |
| `services/`           | 後端 API 接口 | 所有 axios 請求的封裝、整理後端路徑與資料處理。例如：`userService.js`, `orderService.js` 等。 |
| `store/` 或 `pinia/`   | 狀態管理      | 用來儲存共用資料狀態，如使用者登入資訊、購物車、Token 等。搭配 `defineStore()` 使用。               |
| `composables/`        | 自定義邏輯工具   | Composition API 的函式封裝區，例如：`useAuth.js`, `useScroll.js`。              |
| `utils/` 或 `helpers/` | 純邏輯工具     | 非 Vue 專用，純 JavaScript 函式，例如格式轉換、日期處理、字串操作等。                          |
| `constants/`          | 常數定義      | 靜態資料、表單選項、狀態對照表、錯誤訊息等，例如：`statusMap.js`, `formOptions.js`。           |

---

### 🎨 視覺與外觀層級

| 資料夾        | 📦 責任 | 🧠 用途與內容                                                                                |
| ---------- | ----- | --------------------------------------------------------------------------------------- |
| `assets/`  | 靜態素材  | 存放圖片、影片、音效、字型檔等素材。這些資源通常會經過 Vite 編譯。                                                    |
| `styles/`  | 全域樣式  | SCSS / CSS 檔案、reset.css、變數設定、全域字體等。可以搭配 PrimeFlex 或其他工具。                                |
| `layouts/` | 版型外殼  | 放各種頁面框架，例如：`default.vue`, `admin.vue`, `auth.vue`，搭配 `vite-plugin-vue-layouts-next` 使用。 |

---

### 🔌 插件與組合系統層

| 資料夾        | 📦 責任 | 🧠 用途與內容                                                               |
| ---------- | ----- | ---------------------------------------------------------------------- |
| `plugins/` | 插件註冊區 | 初始設定第三方插件，例如 PrimeVue、VeeValidate、i18n 等，讓 `main.js` 更乾淨可維護。           |
| `router/`  | 路由設定區 | 若使用手動設定路由，這裡放 Vue Router 設定檔 `index.js`。若用 `unplugin-vue-router`，可以跳過。 |

---

## 📘 常見命名慣例 × 團隊建議統整表

| 概念         | 建議命名                  | 說明                                    |
| ---------- | --------------------- | ------------------------------------- |
| 狀態管理       | `store/` 或 `pinia/`   | 二選一即可，保持一致性                           |
| API 請求封裝   | `services/` 或 `api/`  | `services/` 較中性，適用所有後端互動              |
| 工具函式       | `utils/` 或 `helpers/` | 用來放邏輯處理，無狀態、無元件關聯                     |
| 常數資料       | `constants/`          | 表單選項、分類清單、錯誤代碼等                       |
| 頁面元件（對應路由） | `views/` 或 `pages/`   | 若用 `unplugin-vue-router` 建議用 `pages/` |

---

### 🧩 小整理：views vs components 的差別？

| 比較項目 | `views/`             | `components/`            |
| ---- | -------------------- | ------------------------ |
| 定位   | 路由對應的「整頁畫面」          | 可重用的小元件                  |
| 類型   | 通常是一整頁 `.vue` 組成     | 小型模組，搭配 props/slot 使用    |
| 用途   | 註冊在路由中，代表「某個網址」顯示的頁面 | 可在多個頁面重複出現（按鈕、列表、Modal）等 |

---

## 🧰 統一資料夾 × 可維護開發結構範本

```
📁 src/
│
├── assets/        # 靜態素材（圖片、樣式）
├── styles/        # SCSS、reset.css、變數
├── components/    # 可重用 UI 元件
├── layouts/       # 多版型 Layout
├── pages/         # Vue 路由頁面（搭配自動路由）
├── plugins/       # 插件註冊區（如 PrimeVue）
├── router/        # 路由設定（如有手動）
├── store/         # 狀態管理（pinia）
├── services/      # axios 請求封裝
├── composables/   # 自定義邏輯工具 useXXX
├── constants/     # 靜態常數與選項清單
├── utils/         # 純邏輯工具（不含 Vue）
├── App.vue        # 根元件
└── main.js        # 主程式初始化
```

---

## ✨ 動畫與輪播功能工具分類總整理

| 套件名稱       | 分類        | 用途說明                 | 特點                          | 適合情境                  |
| ---------- | --------- | -------------------- | --------------------------- | --------------------- |
| `GSAP`     | 明確功能型動畫工具 | 高度自定義、時間線控制精細        | 效能佳、支援複雜動畫組合                | 頁面轉場、捲動動畫、視差動畫        |
| `Swiper`   | 幻燈片輪播工具   | 手機觸控友善、支援輪播圖與步驟切換    | 支援 loop / lazyload / 自定義箭頭等 | 輪播圖、圖片集、步驟導覽          |
| `Anime.js` | 較輕巧動畫工具   | 輕量級、語法簡單，適合小範圍動態效果控制 | 語法直覺、快速上手                   | 圖示、按鈕、數字滾動動畫、hover 效果 |

---

## 📦 安裝指令（作業需求常見）

```bash
npm install gsap swiper animejs
```

---

## 💡 Swiper 補充說明（Vue 使用方式）

> 📌 若你使用的是 Swiper，要特別注意以下整合方式：

### 安裝 Swiper 的 Vue 套件形式

```bash
npm i swiper
```

### 引入方式：

```vue
<script setup>
import { Swiper, SwiperSlide } from 'swiper/vue'
import 'swiper/css'
</script>

<template>
  <Swiper :loop="true" :autoplay="true">
    <SwiperSlide>Slide 1</SwiperSlide>
    <SwiperSlide>Slide 2</SwiperSlide>
  </Swiper>
</template>
```

### 注意事項：

* Swiper 一定要引入樣式檔 `swiper/css`
* 元件名稱需正確（`Swiper`, `SwiperSlide`）
* 搭配 PrimeVue 使用時避免命名衝突，例如不要用 `<Button>` 在 slide 中觸發動畫，會吃掉事件

---

## 🎯 PrimeVue 搭配動畫套件的整合注意

| 動畫套件       | 搭配 PrimeVue 的注意事項                                            |
| ---------- | ------------------------------------------------------------ |
| `GSAP`     | 操作 DOM，請放在 `onMounted()` 裡執行動畫。避免與 `v-if` 或 `v-show` 衝突      |
| `Anime.js` | 一樣需等元件掛載後再操作。適合局部動畫（如 icon 抖動、數字跳動）                          |
| `Swiper`   | 建議搭配 `swiper/vue` 元件方式使用，搭配 PrimeVue 排版工具（如 `primeflex`）避免衝突 |

---

## 🧪 與 Vue 的生命週期搭配建議

```js
import { onMounted } from 'vue'
import gsap from 'gsap'

onMounted(() => {
  gsap.from('.box', { y: 100, opacity: 0, duration: 1 })
})
```

🔁 若動畫沒有作用，通常原因有以下幾個：

1. DOM 還沒掛上（用 `onMounted()` 解決）
2. DOM 被 Vue 處理重渲染（解法：用 `nextTick()` 保證 DOM 真的在）
3. `v-if` 把元素暫時移除 → 建議改用 `v-show` 或 `@after-leave` 等監聽

---

## 🔧 PrimeVue 的 class 搭配建議（排版 + 動畫）

```html
<div class="flex justify-content-center align-items-center">
  <Button label="Start" class="animated-btn" />
</div>
```

你可以這樣在 SCSS 中加動畫類別：

```scss
.animated-btn {
  animation: bounceIn 0.5s ease-out;
}
```

也可以使用 `GSAP.to()` 或 `anime({ targets: ... })` 來控制精細動畫。

---

## ✅ 動畫功能選擇建議總結

| 使用目的                | 建議工具     | 原因與說明                                 |
| ------------------- | -------- | ------------------------------------- |
| **頁面轉場 / 捲動動畫**     | GSAP     | 控制時間軸、支援多段動畫序列                        |
| **輪播圖 / 步驟導覽**      | Swiper   | 手機友善、有元件套件、支援 autoplay                |
| **小動畫 / 數字 / icon** | Anime.js | 輕巧、語法簡單、適合小範圍動畫                       |
| **整合 PrimeVue**     | 皆可       | 但需注意使用 timing（mounted）、樣式 class 會否衝突等 |

---

待補

* 🎬 **動畫觸發方式整理（onMounted / IntersectionObserver / 滾動觸發）**
* 🧱 **PrimeVue + Swiper 範例組合實作**
* 💻 **動畫元件封裝策略（封裝成 composable 或元件）**

---

## 🎬 動畫要怎麼觸發才對？Vue 中的時機整理

| 觸發時機                   | 建議使用方式                                                 | 適合動畫情境                  |
| ---------------------- | ------------------------------------------------------ | ----------------------- |
| `onMounted()`          | 元件一載入就開始動畫                                             | 頁面剛進來就要出現的動畫（首頁載入、進場動畫） |
| `nextTick()`           | DOM 操作後等畫面真正完成，確保 DOM 存在                               | 使用 `v-if` 控制動畫物件顯示/消失時  |
| `IntersectionObserver` | 使用 JS 觀察 DOM 是否「進入畫面範圍」再觸發動畫                           | 滾動動畫、lazy loading、視差動畫  |
| `@scroll` 事件監聽         | 搭配 `window.addEventListener('scroll')` 控制動畫邏輯          | 滾動百分比改變、滾動觸發動畫（但需防抖）    |
| Vue Transition API     | Vue 提供的 `<transition>` 或 `<transition-group>` 套用 class | 進出動畫（滑入滑出、淡入淡出）、列表過場動畫  |

---

## 🔍 `IntersectionObserver` 基礎用法（滾動觸發動畫）

```js
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('animate')
    }
  })
})

onMounted(() => {
  const target = document.querySelector('.section')
  observer.observe(target)
})
```

📌 好處：

* 不需持續監聽 scroll，效能佳
* 可指定元素進入畫面時才觸發動畫
* 適合 GSAP、Anime.js、或純 CSS class 切換用

---

## 🔧 動畫功能可以怎麼封裝？

你可以選擇用「函式 + 組件」的方式，把動畫功能拆成可以重複使用的模組：

### 1️⃣ 封裝為 `composables/useScrollAnimation.js`

```js
// useScrollAnimation.js
import { onMounted } from 'vue'

export function useScrollAnimation(selector, callback) {
  onMounted(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          callback(entry.target)
        }
      })
    })
    document.querySelectorAll(selector).forEach((el) => observer.observe(el))
  })
}
```

在頁面中使用：

```js
import { useScrollAnimation } from '@/composables/useScrollAnimation'

useScrollAnimation('.fade-in', (el) => {
  el.classList.add('animate')
})
```

---

### 2️⃣ 封裝成動畫組件 `<FadeInSection>`

```vue
<template>
  <section ref="el" class="fade-in-section">
    <slot />
  </section>
</template>

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

const el = ref(null)

onMounted(() => {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add('fade-in')
      }
    })
  })
  observer.observe(el.value)
})
</script>
```

用法：

```vue
<FadeInSection>
  <div>這區會滾動進場才出現</div>
</FadeInSection>
```

📌 這種封裝方式讓動畫可重用，方便維護、樣式獨立。

---

## 🧱 與 PrimeVue 的實務搭配建議

| 元件類型          | 動畫使用建議                                                   |
| ------------- | -------------------------------------------------------- |
| `<Dialog>`    | 可使用 Vue 的 `<transition>` 包住，控制淡入淡出                       |
| `<Carousel>`  | 可搭配 Swiper/GSAP 滾動進場時啟動輪播動畫                              |
| `<Button>`    | 搭配 `animejs` 做 hover 動畫、點擊反饋                             |
| `<DataTable>` | 可加進場淡入動畫或欄位浮動動畫（需等資料渲染完成才執行）                             |
| `<TabView>`   | 切換 tab 時使用 `v-show` 搭配動畫 class（或 GSAP 處理 enter/leave 動畫） |

---

## ✅ 動畫開發 Checklist（整合）

* [ ] 確認動畫是否為「進場就啟動」還是「滾動觸發」
* [ ] 動畫套件是否需操作 DOM → 要用 `onMounted()` or `nextTick()`
* [ ] 使用元件是否會破壞 DOM（如 `v-if` 重渲染）→ 改用 `v-show` 或觀察 ref
* [ ] 是否需要封裝 → 重複使用的建議寫成 composable 或元件
* [ ] 是否會影響效能（大量 scroll event）→ 優先使用 IntersectionObserver

---
