# 🧰 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
---