# 前端環境 👀 無用的知識增加了 --- ## 什麼時候建立這些檔案與資料夾 | 項目 | 什麼時候建立? | 說明 | | ----------------- | -------------------- | ------------------ | | `.env` | 建立 server/index.js 前 | 用於設定 PORT、API\_KEY | | `router/index.js` | 安裝 `vue-router` 後 | 控制前端路由 | | `src/services/` | 要與 API 互動時 | 放 axios 相關邏輯 | | `store/` | 需要全域狀態時 | 使用 pinia 管理 | --- ## main.js PrimeVue 在 main.js 註冊 PrimeVue 打開 src/main.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' // 排版工具 const app = createApp(App) app.use(PrimeVue) app.mount('#app') ``` ### 逐行解釋 Vue 的初始化設定 ``` import { createApp } from 'vue' import App from './App.vue' ``` 🔹 createApp:從 Vue 套件裡拿出來的功能,用來建立整個 Vue App(Vue 3 的寫法) 🔹 App.vue:你專案的「主畫面元件」,等於是整個網站的入口頁 --- PrimeVue 的初始化設定 ``` import PrimeVue from 'primevue/config' ``` 🔸 這是把 PrimeVue 這個 UI 元件庫的設定引進來,後面會 .use() 用到 🔸 這樣你才能在每個頁面使用 `<Button>`, `<Dialog>` 等元件 --- 🎨 匯入主題樣式 ``` import 'primevue/resources/themes/lara-light-blue/theme.css' ``` 這是你網頁的「整體配色與風格」,例如按鈕長什麼樣、背景色、陰影等等 你可以換成其他主題(像 lara-dark-indigo),一行換風格。 --- 📦 匯入 PrimeVue 所有元件的基本樣式庫 ``` import 'primevue/resources/primevue.min.css' ``` 這是必要的。 就像衣服的基本材質,有這行你才能看到按鈕長出外型。 --- 🔠 匯入圖示庫(Icons) ``` import 'primeicons/primeicons.css' ``` 使用範例 ``` <i class="pi pi-check"></i> ← ✅ 圖示 ``` --- 🧱 匯入 PrimeVue 的 CSS 工具集 ``` import 'primeflex/primeflex.css' ``` 使用範例 ``` <div class="flex justify-content-between align-items-center">...</div> ``` --- 把前面所有東西「組起來並啟動」 ``` const app = createApp(App) app.use(PrimeVue) app.mount('#app') ``` | 步驟 | 說明 | | ---------------- | ----------------------------------------- | | `createApp(App)` | 建立整個 Vue App | | `.use(PrimeVue)` | 啟用 PrimeVue 元件系統 | | `.mount('#app')` | 把整個網站掛在 `index.html` 的 `<div id="app">` 裡 | 🧩 小提醒:這段只做「主程式初始化 + UI 套件載入」 之後你還需要在每個要用元件的 .vue 檔中個別匯入元件才能使用。 ### PrimeVue 元件使用方式小提醒 很多 UI 套件需要註冊個別元件,例如: ``` import Button from 'primevue/button' app.component('Button', Button) ``` 元件註冊方式的兩種策略: * 單一註冊(個別引入)→ 效能佳 * 全域註冊(註冊所有常用元件)→ 開發快速 可搭配: ``` // plugins/primevue.js import Button from 'primevue/button' app.component('Button', Button) ``` 可以集中管理註冊過的元件。 --- ## 誰規定了什麼 Vue / Vite / UI框架 🧠 一次釐清:誰規定了什麼? | 語句 | 誰規定的 | 為什麼要這樣寫 | | -------------------------------------------------- | ------------ | ------------------------------------------- | | `import { createApp } from 'vue'` | ✅ Vue | Vue 3 官方的 App 初始化方式(Vue 2 是用 `new Vue()`) | | `import App from './App.vue'` | ✅ Vue + Vite | Vue 支援 `.vue` 檔案語法,Vite 幫忙處理這類 import | | `import PrimeVue from 'primevue/config'` | ✅ PrimeVue | 這是 PrimeVue 定義的初始化方法,要先 `.use(PrimeVue)` | | `import 'primevue/resources/themes/xxx/theme.css'` | ✅ PrimeVue | 每個主題的 CSS 都被獨立放在這個資料夾下 | | `import 'primevue/resources/primevue.min.css'` | ✅ PrimeVue | 所有元件都共用的「基本樣式表」 | | `import 'primeicons/primeicons.css'` | ✅ PrimeIcons | PrimeVue 圖示庫是獨立的套件,必須自己引入 | | `import 'primeflex/primeflex.css'` | ✅ PrimeFlex | CSS 工具集,像 Tailwind 一樣需要你手動引入 | | `app.use(PrimeVue)` | ✅ Vue(設計模式) | Vue plugin 設計規定,要用 `.use()` 方式註冊第三方功能 | | `app.mount('#app')` | ✅ Vue | Vue 的啟動規則,將 app 掛在 HTML 中的 `<div id="app">` | 🔍 各角色負責什麼? 🟦 Vue * 定義了 createApp()、.use()、.mount() 等生命週期方法 * 決定你怎麼寫 .vue 檔案 * 你在 .vue 中用的 `<template>`、`<script>`、`<style>` 都是 Vue 定的 🟨 Vite * 處理 import 行為,讓你可以直接寫 .vue / .css / 第三方元件 * 支援熱更新(Hot Reload)、打包、開發伺服器等 * 負責讓你開啟 `http://localhost:????` 就能看到畫面 🟩 PrimeVue * 提供元件(Button、Dialog、Table...) * 規定你要如何引入元件(import)、初始化(`PrimeVue from 'primevue/config'`) * 自家設計的主題與圖示庫要自己引入 --- 🧩 總結一口氣記住: | 領域 | 誰決定的? | 代表規則 | | ----------------------- | -------- | ---------------------- | | 應用啟動方式(createApp、mount) | Vue | App 怎麼啟動、掛載 | | 組件設計與 .vue 語法 | Vue | `<template>` 怎麼寫 | | `import` / 模組處理 | Vite | 支援 CSS/圖示/vue 組件匯入 | | UI 元件、主題、class 名稱 | PrimeVue | 要怎麼寫 `<Button>`、要引哪些樣式 | 三者關係 Vue(主導架構) ← Vite(打包與開發助手) ← PrimeVue(元件 UI 借給你用) --- ## 常駐工具型功能款總整理(LIST) - HTTP 請求:`axios` - 表單驗證:`vee-validate`, `yup` - 狀態管理:`pinia` - 狀態儲存:`pinia-plugin-persistedstate` - Icon 擴充:`@iconify/vue` - 日期處理:`dayjs` --- HTTP 請求 axios 向 API 取資料、送表單用 ``` npm i axios ``` --- vee-validate + yup 表單驗證 預設表單驗證搭配。前端專案幾乎都會有表單 ``` npm i vee-validate yup ``` --- pinia 狀態管理 Vue 官方推薦的 state 管理工具(新版 Vuex) ``` npm i pinia ``` --- pinia-plugin-persistedstate 狀態儲存 狀態資料存到 localStorage,記住登入等狀態 ``` npm install pinia-plugin-persistedstate ``` --- @iconify/vue Icon 額外擴充 ``` npm install @iconify/vue ``` --- dayjs(或 date-fns) 日期處理 處理日期字串、轉格式、相對時間等 ``` npm install dayjs ``` --- ## pinia 註冊方式補充(main.js) 註冊方式: ``` // main.js 中加上 import { createPinia } from 'pinia' const pinia = createPinia() app.use(pinia) ``` --- ## pinia-plugin-persistedstate 的使用方式補充 註冊方式: ``` import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' pinia.use(piniaPluginPersistedstate) ``` --- ## unplugin-auto-import `unplugin-auto-import` 是在 **Vite / Vue / Nuxt / Rollup / Webpack** 等環境中常用的 **自動引入套件**,以下是它的介紹與用法整理 👇 ### 📦 `unplugin-auto-import` 是什麼? 它是一個 **自動幫你引入 import 的工具**。 #### 🧠 背後動機: 開發 Vue 或其他框架時,常常會重複寫這種東西: ```js import { ref, computed } from 'vue' ``` 這種語法重複又容易忘記,加上 IDE 有時會提示但不自動補全,所以這個套件就是: 👉 **讓你用 `ref()` 就好,不用手動 import `ref`** ### 🔧 安裝方式(以 Vite 為例) ```bash npm i -D unplugin-auto-import ``` ### ⚙️ 在 Vite 設定中使用(vite.config.js) ```js import AutoImport from 'unplugin-auto-import/vite' export default { plugins: [ AutoImport({ // 📚 常用的自動引入來源 imports: [ 'vue', // ref, reactive, computed... 'vue-router', // useRoute, useRouter... 'pinia', // defineStore... ], dts: 'src/auto-imports.d.ts', // 👉 自動產生的 TypeScript 提示檔 }), ] } ``` ### ✅ 自動引入範例 你可以直接寫: ```js const count = ref(0) const doubled = computed(() => count.value * 2) ``` 不需要手動 import! ### 📘 支援什麼? 你可以自定義要引入的內容,例如: ```js AutoImport({ imports: [ { axios: [ ['default', 'axios'] // 讓你可以直接寫 axios() ] } ] }) ``` 還可以配合: * `eslint` 的全域變數自動註冊 * 自動引入 `api/` 或 `composables/` 裡的函式(進階用法) ### ⚠️ 注意事項 * 如果你使用的是 Vue + `<script setup>`,其實也可以用 `<script setup>` 的自動解構,但 `unplugin-auto-import` 更廣泛且跨框架 * 要搭配 TypeScript 的話,記得開啟 `dts` 來讓編輯器支援提示 --- --- ## unplugin-vue-components `unplugin-vue-components` 是 Vue 開發中常見的 **自動註冊元件套件**,常跟 `unplugin-auto-import` 搭配使用,下面我幫你整理一份實用說明 👇 ### 📦 `unplugin-vue-components` 是什麼? > 🔧 讓你**不用手動 import 與註冊 Vue 元件**,就能直接使用元件! ### 🧠 使用動機 通常我們在 Vue 專案中這樣引入元件: ```js import MyButton from '@/components/MyButton.vue' export default { components: { MyButton } } ``` 但隨著專案變大,這些 import 與註冊會變得很繁瑣, 這個套件讓你直接在 `.vue` 裡寫 `<MyButton />`,它會幫你自動載入與註冊。 ### 🚀 安裝 ```bash npm i -D unplugin-vue-components ``` ### ⚙️ 基本設定(Vite) 在 `vite.config.js` 中加入: ```js import Components from 'unplugin-vue-components/vite' export default { plugins: [ Components({ // 🔍 掃描目錄(預設就是 'src/components') dirs: ['src/components'], // 產生 TypeScript 自動提示 dts: 'src/components.d.ts' }) ] } ``` ### ✅ 用法範例 專案中有個 `src/components/MyButton.vue`: ```vue <!-- 你可以直接用,不需 import --> <template> <MyButton /> </template> ``` ### 🌟 支援 UI 框架自動引入(超方便) 如果你使用 PrimeVue、Vuetify、Element Plus、Ant Design Vue 等元件庫, 可以加上 `resolvers` 自動引入樣式與註冊元件! #### 例如 PrimeVue 設定: ```bash npm i -D unplugin-vue-components npm i -D unplugin-auto-import npm i -D unplugin-vue-components/resolvers ``` ```js import Components from 'unplugin-vue-components/vite' import { PrimeVueResolver } from 'unplugin-vue-components/resolvers' export default { plugins: [ Components({ resolvers: [PrimeVueResolver()], dts: 'src/components.d.ts', }), ] } ``` ### 🛠️ 進階用法 可以掃描多個元件目錄: ```js dirs: ['src/components', 'src/base-components'] ``` ### ⚠️ 注意事項 * 不支援 **動態元件名稱**(如 `<component :is="compName" />`)→ 要自己 import * 自動掃描元件可能會略影響編譯速度(大量元件時) * `components.d.ts` 記得加進 `.gitignore` 或 version control 依情況決定 --- --- ## unplugin-vue-router `unplugin-vue-router` 是 Vue 3 開發中**新一代的路由自動生成工具**,如果你覺得 `vue-router` 的手動寫法很囉唆,這個套件可以幫你**自動根據檔案結構產生路由**,邏輯有點像 Nuxt.js / Next.js 的檔案式路由,但保有完全客製彈性 ✨ ### 📦 `unplugin-vue-router` 是什麼? > ⛩ 自動根據 `/src/pages` 資料夾產生 `vue-router` 路由表, > 不用自己寫 `routes = [...]` 陣列啦! ### 🧠 為什麼使用它? #### 傳統 vue-router 的痛點: ```js const routes = [ { path: '/', component: Home }, { path: '/about', component: About }, ... ] ``` 重複寫很累,還要手動 import 元件 🤯 👉 `unplugin-vue-router` 幫你「**掃描檔案 → 自動生成路由 → 支援型別推論**」。 ### 🛠️ 安裝 ```bash npm i -D unplugin-vue-router ``` ### ⚙️ 基本設定(Vite) ```js // vite.config.js import VueRouter from 'unplugin-vue-router/vite' export default { plugins: [ VueRouter({ routesFolder: 'src/pages', // 預設掃描頁面元件的資料夾 dts: 'src/typed-router.d.ts', // TypeScript 推論 }) ] } ``` ### 📂 檔案式路由結構範例 ``` src/ ├─ pages/ │ ├─ index.vue -> 路由:/ │ ├─ about.vue -> 路由:/about │ └─ blog/ │ ├─ [id].vue -> 路由:/blog/:id ``` ### 🧩 如何使用? 在 `main.js` 裡改用它產生的 router: ```js import { createApp } from 'vue' import App from './App.vue' // ↓ 使用這個 router 替代 vue-router 自己寫的 import { createRouter, createWebHistory } from 'vue-router/auto' const app = createApp(App) const router = createRouter({ history: createWebHistory(), }) app.use(router) app.mount('#app') ``` ### 🧠 額外強項 * 支援 `definePageMeta()` → 可在元件中自訂 `meta` * 支援 TypeScript 推論所有路由 * 支援中間層資料夾作為 Layout(像 Nuxt) * 可客製擴充:你還是能手動新增路由或混合使用 ### 📌 VS 傳統 vue-router | 功能 | 傳統 vue-router | unplugin-vue-router | | ------------ | ------------- | ------------------- | | 自動產生路由 | ❌ | ✅ | | 檔案路由 | ❌ | ✅ | | 支援型別提示 | 部分 | ✅ | | 中間 layout 支援 | ❌ | ✅ | | Nuxt/Next 風格 | ❌ | ✅ | ### ⚠️ 注意事項 * 你仍需使用 `vue-router@4`,這是它底層的核心。 * 如果你使用 `<script setup>` 可以搭配 `useRoute()`、`useRouter()`,同樣能支援推論。 * `src/pages` 是它預設掃描的資料夾,**不能亂放**頁面元件。 * 有些情境仍需要 `definePageMeta()` 或手動 route 擴充(例如設定 `meta.requiresAuth`) 如果你有使用 `unplugin-auto-import` 的話,可以一起補上: ```js import AutoImport from 'unplugin-auto-import/vite' AutoImport({ imports: [ 'vue', 'vue-router/auto', // 自動引入 useRoute / useRouter 且支援 typed router ], dts: 'src/auto-imports.d.ts', }) ``` --- --- ## vite-plugin-vue-layouts-next `vite-plugin-vue-layouts-next`,這是 Vue 3 + Vite 專案中,**用來實現「多版型(多 Layout)」功能的套件**,非常適合搭配像 `unplugin-vue-router` 這種自動路由系統使用。 --- ### 📦 `vite-plugin-vue-layouts-next` 是什麼? > 幫你實現「版型管理系統」,例如: > > * 登入頁使用 `AuthLayout.vue` > * 管理後台頁使用 `AdminLayout.vue` > * 一般使用者頁使用 `DefaultLayout.vue` 它會依據你在頁面元件中設定的 layout 名稱,自動套用對應的版型。 --- ### 🧠 解決什麼問題? 在大型 Vue 專案中,不同頁面常常有不同外觀: * 有些頁面有 sidebar、有些沒有 * 有些頁面是全畫面,有些有 header/footer 自己用 `v-if` 切換 layout 很麻煩,這套件幫你自動搞定。 --- ### 🛠️ 安裝 ```bash npm i -D vite-plugin-vue-layouts-next ``` --- ### ⚙️ 基本設定(vite.config.js) ```js import VueLayouts from 'vite-plugin-vue-layouts-next' export default { plugins: [ VueLayouts({ layoutsDirs: 'src/layouts', // 預設 layouts 放這裡 defaultLayout: 'default', // 沒寫的話就用 default.vue }) ] } ``` --- ### 🧩 使用方式 #### 1. 建立 Layouts 結構(在 `src/layouts`) ``` src/ ├─ layouts/ │ ├─ default.vue │ ├─ auth.vue │ └─ admin.vue ``` 每個 Layout 都是一個元件,像這樣 👇 ```vue <!-- src/layouts/default.vue --> <template> <div> <Header /> <main> <slot /> </main> <Footer /> </div> </template> ``` --- #### 2. 頁面元件設定 Layout 在 `src/pages/` 裡的某個頁面元件: ```vue <!-- src/pages/dashboard.vue --> <script setup> definePageMeta({ layout: 'admin' // 🔁 這頁會套用 admin.vue }) </script> <template> <h1>後台頁面</h1> </template> ``` --- ### 📦 常搭配組合 | 功能 | 套件 | | ----------- | ------------------------------ | | 自動產生路由 | `unplugin-vue-router` | | 自動註冊 Layout | `vite-plugin-vue-layouts-next` | | 自動註冊元件 | `unplugin-vue-components` | | 自動引入常用函式 | `unplugin-auto-import` | --- ### 📌 完整搭配範例流程 ```bash npm i vue-router@4 npm i -D unplugin-vue-router vite-plugin-vue-layouts-next ``` 接著搭配如下: #### `vite.config.js` ```js import VueRouter from 'unplugin-vue-router/vite' import VueLayouts from 'vite-plugin-vue-layouts-next' export default { plugins: [ VueRouter({}), VueLayouts({}) ] } ``` #### `main.js` ```js import { createApp } from 'vue' import App from './App.vue' import { createRouter, createWebHistory } from 'vue-router/auto' import { setupLayouts } from 'virtual:generated-layouts' const router = createRouter({ history: createWebHistory(), extendRoutes: setupLayouts, // 🚨 這句很重要!自動套 layout }) const app = createApp(App) app.use(router) app.mount('#app') ``` --- ### ⚠️ 注意事項 * Layout 檔案的檔名不要用大寫開頭(可能會抓不到) * 必須搭配 `vue-router` 使用(尤其建議搭配 `unplugin-vue-router`) * `definePageMeta()` 是由 `unplugin-vue-router` 提供的 → 所以這兩個通常一起用 --- ## unplugin-fonts 這是一個非常實用的小工具,尤其適合懶得手動搞 Google Fonts 的人 ### 🎯 `unplugin-fonts` 是什麼? > 👉 它是一個 **自動幫你載入 Web 字型**(通常是 Google Fonts)的 Vite 插件, > 讓你不需要在 HTML 裡加 `<link>`,也不必自己下載字型檔。 --- ### ✅ 使用動機 原本你得這樣手動在 `index.html` 裡寫: ```html <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet"> ``` 但這很麻煩,而且不同字型、不同字重、不同語系還要手動拼 URL 😩 👉 裝了 `unplugin-fonts` 後,只要在 `vite.config.js` 裡設定: ```js Fonts({ google: { families: ['Roboto'] } }) ``` 它就會幫你: * 自動載入 Google Fonts * 幫你生成正確的 font-face、權重參數 * 自動注入 `<link>` 到頁面(或用 CSS import) --- ### 🛠️ 安裝方式 ```bash npm i -D unplugin-fonts ``` --- ### ⚙️ 基本設定範例 ```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', }, ] } }) ] }) ``` --- ### 🤔 它做了什麼? | 功能 | 說明 | | ---------------------- | --------------------- | | ✅ 自動產生 Google Fonts 連結 | 幫你拼好網址 | | ✅ 注入 HTML | 自動幫你加進 `<head>` 裡 | | ✅ 支援多個字型與權重 | `wght@400;700` 等 | | ✅ 支援自訂 provider | 預設是 Google,也可自訂或切換成本地 | --- ### ⚠️ 注意事項 * 這是**開發用超方便工具**,但在**正式專案中**建議還是針對字型做更精細的優化(例如自行下載、使用 subset) * 預設是載入 Google Fonts,如果你想切成 CDN / 自架字型,也可以設定 `custom` provider * 不會自動把字型套用到你專案,還是要自己在 `main.css` 或 `App.vue` 裡加: ```css body { font-family: 'Roboto', sans-serif; } ``` --- ### ✅ 是否通用? 完全通用 ✔️ | 使用框架 | 是否支援 | | ------------------------ | ----------------------- | | Vue + Vite | ✅ | | React + Vite | ✅ | | Nuxt / Astro / SvelteKit | ⚠️ 不一定支援,需手動整合 | | Webpack | ❌ 官方只提供 Vite/ESBuild 入口 | --- ### 🧾 小結 | 問題 | 答案 | | ------------- | --------------------------------- | | 是做什麼的? | 幫你自動載入 Google Fonts,不用寫 `<link>` | | 推不推薦? | ✅ 如果你用 Google Fonts 且想省事,這套件超讚 | | 可以拿掉嗎? | 可以,如果你改成手動載入或不需要字型了 | | 能跟 UI 套件一起用嗎? | ✅ 可以補上字型搭配主題(如 Roboto 配 PrimeVue) | --- ## 後端初始化 server/index.js ``` // server/index.js(最小 Express 範例) 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 ``` --- --- ## Vite → Proxy 🧭 問題背景:為什麼需要 proxy? 當跑 Vite 前端時,會開在: ``` http://localhost:5173 ``` 而跑後端 Express 時,會開在: ``` http://localhost:3000 ``` 這時候如果在前端裡寫: ``` axios.get('http://localhost:3000/api/hello') ``` 就會遇到這個錯誤: >` ❌ Access to fetch at 'http://localhost:3000/api/hello' from origin 'http://localhost:5173' has been blocked by CORS policy` 原因: 因為前後端「不同源」(不同 port),瀏覽器預設禁止跨域請求。 ✅ 解法:用 Vite 的 proxy 幫你偷偷轉送請求 前端這樣寫: ``` axios.get('/api/hello') // 沒有寫 http://localhost:3000 ``` 然後在 Vite 設定中這樣寫: (vite.config.js) ``` // vite.config.js export default { server: { proxy: { '/api': 'http://localhost:3000' } } } ``` 意思是: > ❗「當你請求 /api/xxx,我會幫你偷偷轉送到 `http://localhost:3000/api/xxx`」 這樣前端看起來就像自己處理,但實際上請求是送給後端的。 📌 什麼時候要用 Vite proxy? | 使用時機 | 原因 | | -------------------------- | --------------------- | | 前後端開發分開時(不同 port) | 避免跨域錯誤(CORS) | | 想要前端 axios 寫簡單 `/api/...` | 保持代碼環境一致,方便日後部署 | | 後端是自己寫的(express) | 你可以控制本地 port,也能接收這些請求 | ✅ 一個完整範例流程 1. 後端:Express 設一個 API ``` // server/index.js app.get('/api/hello', (req, res) => { res.json({ message: 'hello from server' }) }) ``` 2. 前端:Vue 組件中使用 axios ``` axios.get('/api/hello') .then(res => console.log(res.data.message)) ``` 3. Vite 設定 proxy: ``` // vite.config.js export default { server: { proxy: { '/api': 'http://localhost:3000' } } } ``` ✅ 成功!你不需要處理 CORS、也不用寫死 IP,前端開發乾乾淨淨。 --- ## axios 的 baseURL 設定方式 .env 變數範例 .env ``` VITE_API_BASE_URL=http://localhost:3000 ``` axios 寫法 ``` const baseURL = import.meta.env.VITE_API_BASE_URL ``` --- --- ## 寫的順序跟實際上跳轉的順序 🎯 情境設定 >🔧 專案使用 Vite(Vue)為前端、Express 為後端,有 .env 檔、可能有 API 溝通。 🛠 該從哪裡開始寫? | 起手區塊 | 說明 | 路徑建議 | | ------------------- | ---------------------------- | ---------------------------- | | ✅ `server/index.js` | 後端 API 入口 | `/server/index.js` | | ✅ `client/` | Vite + Vue 前端專案 | `/client/` 是完整前端資料夾(像你現在的專案) | | ✅ `.env` | 後端的機密設定(例如 PORT、API KEY) | 放在根目錄 `/` | | ✅ `.gitignore` | 忽略 `.env` / `node_modules` 等 | 放根目錄 | 可以先寫後端伺服器(server/index.js)→ 確保能回傳資料 → 再寫前端來抓資料 🔁 前後端程式運作順序圖(超重要!) ``` [瀏覽器開啟網頁] ↓ [前端:Vite 伺服器] ← Vue 負責畫面 ↓ (向後端請求資料) [後端:Express 伺服器] ← 接收請求、回應 JSON ↓ [傳資料回前端] ↓ [Vue 畫出資料] ``` ✅ 所以前端主要負責: * 畫畫面(Vue + PrimeVue) * 抓資料(axios 去 call Express API) 後端主要負責: * 接收前端請求(用 app.get()、app.post()) * 回應資料(資料庫 or 假資料) ✏️ 實際檔案撰寫順序(最推薦) 1. 先寫後端` server/index.js`: > 建 Express app > 建一個測試 API,例如 /api/hello > 用 nodemon 跑起來測試能不能回應資料 2. 再寫前端 `client/` 專案(你已經建立好了) > 加 axios > 在 Vue 組件中去 GET /api/hello > 顯示回傳資料 3. 加 .env 設定 `port(ex: 3000)`: ``` PORT=3000 ``` 4. 在前端 `vite.config.js` 加上 proxy,讓 `/api/` 轉向後端: ``` // vite.config.js export default defineConfig({ server: { proxy: { '/api': 'http://localhost:3000' } } }) ``` 所以整個 `vite.config.js` 的完整範本 ``` // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { proxy: { '/api': 'http://localhost:3000' } } }) ``` 🧩 總結 ``` 📁 server/  └─ index.js ← 🥇 從這開始寫(提供 API) 📁 client/  └─ 已建好(Vite + Vue)← 🥈 接著寫這邊,抓 API 📄 .env  → 提供後端參數 📄 vite.config.js  → 幫前端 proxy 到後端 ``` ❓你現在可以問自己: * 我想要前端從哪裡「抓資料」?(→ 後端 API 要先寫) * 我要不要測試前後端能不能對話成功?(→ 用 axios call /api/hello 看看) * 我的 .env 有提供後端啟動資訊嗎?(→ 設定 PORT) --- ## vite.config.js 的簡單用途 如果有些套件需要設定 alias(像 @/views/...): ``` // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import from 'path' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, './src') } } }) ``` 多環境處理補充 後來有 dev / staging / production 時,可以補充這段 ``` // vite.config.js const isDev = process.env.NODE_ENV === 'development' proxy: { '/api': isDev ? 'http://localhost:3000' : 'https://my-prod-api.com' } ``` --- ## axios 封裝 service 範例(加強 /services/ 用法) 🔁 axios 攔截器(interceptors):可在每次發送/接收請求前統一處理資料,例如自動加上 Authorization token,或是統一處理錯誤訊息。 例如:建立 /services/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 的路徑與格式 error 處理建議 ``` export const fetchUser = async (id) => { try { const res = await axios.get(`${baseURL}/user/${id}`) return res.data } catch (error) { console.error('❌ 無法取得使用者資料', error) throw error } } ``` --- ## App.vue 裡預設的 router-view(路由出口) ``` <template> <router-view /> </template> ``` > ☑ 用途:Vue Router 的畫面顯示容器,對應你設定的 routes --- ## 每個資料夾負責什麼 > Vue 專案的資料夾結構會因人而異,但在實務開發中,已經發展出一套非常清楚的責任分工慣例 🗂 Vue 專案中每個資料夾的常見用途(建議使用習慣) | 資料夾名稱 | 📦 責任類型 | 🧠 負責什麼事 | 範例與說明 | | ------------------------ | -------- | -------------------------------------------------- | ---------------------------------------------- | | `📁 assets/` | 📁 靜態資源 | 存放圖片、音效、影片、樣式檔(SCSS、CSS) | 不經 JavaScript 處理,可直接在 `<img src>` 中用 | | `📁 components/` | 🧩 共用元件 | 放「可重複使用」的小元件(按鈕、卡片、標籤等) | 通常會配合 `props` 與 `slots` 提高彈性 | | `📁 views/` | 🧱 頁面元件 | 放「對應路由」的大畫面(像首頁、商品頁、表單頁) | 一頁一個 `.vue`,對應 `router` 裡的 path | | `📁 router/` | 🚦 路由控制 | 放 Vue Router 設定檔(定義頁面對應路徑) | 最常見是 `router/index.js` 或 `router.js` | | `📁 store/` | 🧠 狀態管理 | 管理應用程式的共用資料(如登入狀態、購物車) | 若用 `pinia`,這裡放 `userStore.js`、`cartStore.js` 等 | | `📁 services/` | 🔁 後端互動 | 負責 API 請求的封裝(如 axios 請求) | 像是 `api.js`、`userService.js`、`orderService.js` | | `📁 composables/` | 🧩 可組合邏輯 | 放 `useXXX()` 型的邏輯抽取函式(Vue 3 的 `setup()` 工具) | e.g. `useAuth.js`, `useScroll.js` | | `📁 plugins/` | 🔌 插件註冊 | 放第三方套件註冊(ex: vee-validate、i18n) | 在 `main.js` 中集中註冊與初始化 | | `📁 layouts/` | 🏗 結構外殼 | 若有使用 layout 機制(像 Nuxt),可放固定結構模板(如 navbar + footer) | 有時會搭配 slot 與動態內容 | | `📁 utils/` 或 `helpers/` | 🧮 工具函式 | 存放純邏輯工具(格式轉換、計算、字串處理) | 不牽涉 Vue,只處理資料 | | `📁 constants/` | 📑 常數資料 | 存放靜態字典、分類表、選項清單 | 例如地區列表、狀態對照表 | 🧩 小提醒:幾個常見易混淆的區塊 | 比較項目 | `components/` | `views/` | | ---- | ----------------------------- | ------------------------------ | | 用途 | 可重用的組件 | 對應頁面的單位 | | 尺寸 | 小顆、模組化 | 大顆、可能包多個組件 | | 例子 | `MyButton.vue`、`UserCard.vue` | `HomeView.vue`、`LoginPage.vue` | 📦 如果你要做分頁專案或多人開發,建議你也這樣設計: ``` 📁 src/ │ ├── 📁 assets/ # 靜態圖像與樣式 ├── 📁 constants/ # 常數資料 ├── 📁 components/ # 可重用 UI 元件 ├── 📁 plugins/ # 套件註冊 ├── 📁 layouts/ # 版型 ├── 📁 pages/ # 頁面元件(對應路由) ├── 📁 router/ # 路由設定 ├── 📁 services/ # API 請求封裝 ├── 📁 store/ # 狀態管理(pinia) ├── 📁 composables/ # Vue Composition API 用的自訂邏輯 ├── 📁 utils/ # 輔助函式 └── main.js # 入口程式 ``` 🧰 資料夾命名慣例對照表 | 概念 | 常見命名 | 備註 | | ------ | ------------------- | --------------- | | 狀態管理 | `store` / `pinia` | 建議統一,避免混用 | | API 請求 | `services` / `api` | `services` 比較通用 | | 工具函式 | `utils` / `helpers` | 視團隊習慣而定 | --- ## 📁 Vue 資料夾 × 功能對照表 | 資料夾名稱 | 功能角色 | 負責任務說明 | | -------------- | ---------------------- | ---------------------------------------------------- | | `src/` | 專案主體根目錄 | 所有 Vue 程式碼都放這,以下皆為此目錄下的子資料夾 | | `views/` | 📺 頁面元件 | 一個頁面一個 `.vue`,對應到路由設定(如首頁、登入頁、訂單頁) | | `components/` | 🧩 可重用元件 | 可在多頁共用的小單位(如按鈕、modal、navbar、表單欄位) | | `router/` | 🚦 頁面路由設定 | Vue Router 註冊點,定義網址對應哪些頁面元件(`index.js`) | | `pinia/` | 🧠 全域狀態管理 | Vue 的狀態儲存區(如登入狀態、購物車內容等),內含 store 模組 | | `services/` | 🔁 API 請求模組 | axios 請求封裝區,統一集中管理 API 呼叫(如 `/api/order.js`) | | `assets/` | 🖼 圖片 / CSS 等靜態資源 | 存放樣式檔、圖片、字型等靜態素材 | | `layouts/` | 📐 頁面共用框架(選用) | 若你有 header/sidebar 等重複區塊,可抽出來放這裡(非必要,視專案規模決定) | | `composables/` | 🪝 自定義組合函式(選用) | Vue 3 中用 `setup()` 時常會寫的 `useXXX()` 模組,可集中寫重複邏輯 | | `store/` | ✅ 同 `pinia/`,若你寫成這樣也可以 | `store/` 是常見 pinia 資料夾名稱,也有些人寫成 `stores/`,建議你選一種寫法統一 | | `constants/` | 📑 常數定義區(選用) | 放不會變動的資料(如 API baseURL、表單選項清單、錯誤訊息對照) | | `utils/` | 🛠 小工具函式(選用) | 處理資料轉換、日期處理、字串轉換等通用函式(跟 services 最大差異是:不會打 API) | public/:放「不經過 Vite 處理、會直接放到打包輸出」的靜態檔案,如 favicon、robots.txt、Google 驗證檔等。 --- ## 所有常見初始化組合 ### .vue 組件結構補充(模板 / 腳本 / 樣式) ``` <template> <div>{{ msg }}</div> </template> <script setup> const msg = 'Hello World' </script> <style scoped> div { color: red; } </style> ``` ### vite.config.js 初始化範例 需要加 `@vitejs/plugin-vue` 才能解析 `.vue` ``` import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: \[vue()], resolve: { alias: { '@': path.resolve(\_\_dirname, './src') } }, server: { proxy: { '/api': '[http://localhost:3000](http://localhost:3000)' } } }) ```` ### `main.js` 中的 `vue-router` 與 `pinia` 初始化流程 ``` import router from './router' // 假設你 router 設在 src/router/index.js app.use(router) ``` ### `.env` 與 `import.meta.env` 的更多範例 ``` VITE_APP_TITLE=我的專案 VITE_API_BASE_URL=http://localhost:3000 ``` * 所有以 `VITE_` 開頭的變數才會被 Vite 匯入前端使用。 * `import.meta.env.VITE_APP_TITLE` 在 Vue 組件內也可以用(例如設定標題) --- ## `.gitkeep` 小知識 `.gitkeep` 是為了讓空資料夾能被 Git 追蹤(因 Git 不會記錄空資料夾) --- ## 部屬小記 > * Vite + Vue 前端 → Netlify / Vercel > * Express 後端 → Render / Railway | 部署平台 | 適合用途 | 備註 | | ------- | ------------- | -------------------- | | Netlify | 前端專案 | 免伺服器、適合 Vite、單頁應用 | | Vercel | 前端(支援 Nuxt) | 支援 SSR、也能跑 Vue | | Render | 後端 Express 專案 | 支援 Node.js + 資料庫整合部署 | --- ## package.json 的角色簡介 package.json:記錄目前專案的「套件清單、版本、執行指令、開發資訊」等,幾乎是專案的身份證。 --- ## process.env.NODE_ENV 的用途 在後端 / Vite 開發中,常用來區分 dev / prod,例如: ``` if (process.env.NODE_ENV === 'development') { console.log('你在開發環境中'); } ``` --- ## 常見 VSCode 套件 * Vetur 或 Volar(Vue 支援) * Prettier - Code formatter * ESLint * Path Intellisense * Auto Import * Iconify IntelliSense(若使用 @iconify/vue) --- ## 專案結構總覽圖 ``` 📁 專案結構總覽(建議格式): vite-project/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── plugins/ │ ├── styles/ │ ├── pages/ (搭配 unplugin-vue-router) │ ├── layouts/ (搭配 vite-plugin-vue-layouts-next) │ ├── router/ │ ├── composables │ ├── utils (或 helpers/) │ ├── services/ │ ├── store/ (或 pinia/) │ ├── app.vue │ └── main.js ├── .env ├── .gitignore ├── vite.config.js ├── package.json └── index.js ``` --- ## 常見 FAQ / 小提醒 | 主題 | 建議補充內容 | | --------------------------- | ----------------------------------------------------- | | PrimeVue 表單搭配方式 | 你提到 `vee-validate`,可以簡列 `InputText` + `yup` 整合方式範例 | | 環境變數 `.env` | 補充 `.env` 無法被前端讀取時可能是沒加 `VITE_` prefix | | `axios.defaults.baseURL` 寫法 | 補充「全局設定 baseURL」與「每次呼叫補上 baseURL」差異與時機 | | pinia vs vuex 的簡略對照 | 若讀者背景是從 Vue2 來的話會常問 | | 如何部署到 Vercel / Render | 你最後雖有提,但可補個 1~2 句:「Netlify 適合純前端,Render 可接後端 Express」 | --- ## 常見錯誤對照表 | 錯誤訊息 | 可能原因 | 解法 | | -------------------------- | ----------------- | --------------------------- | | `CORS blocked` | 前後端不同源 | 加 Vite proxy | | `Cannot find module 'vue'` | 沒有安裝 Vue | `npm i vue` | | `Cannot GET /api/...` | Express 沒接收到 | 確認 app.get() 有設定、server 有啟動 | | `404: router-view 沒顯示` | 路由沒設好或 router 未註冊 | 確認 router 有 `.use()` | | 錯誤訊息 | 原因 | 解法 | | ------------------------ | ----------------- | ------------------------- | | CORS blocked | 前後端不同 port | 加 Vite Proxy | | Cannot find module 'vue' | vue 沒裝 | `npm i vue` | | router-view 沒顯示 | router 未註冊、路徑錯誤 | 確認 router 有 `.use()`、檔案存在 | | .env 無效 | 未加 `VITE_` prefix | 改為 `VITE_API_KEY=...` | ---