# 前端環境 👀 無用的知識增加了
---
## 什麼時候建立這些檔案與資料夾
| 項目 | 什麼時候建立? | 說明 |
| ----------------- | -------------------- | ------------------ |
| `.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=...` |
---