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