# Vue 前端環境相關 相關筆記 {%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/rkSzAokSll %} {%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/B1zl12yHlx %} # 🧰 專案初始化與安裝流程 ## 1️⃣ 建立環境(系統框架與開發基礎) 📦 定義:為了讓整個專案能「順利執行、順利開發」的底層工具 - {%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/r14DVAtveg %} - {%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/Hkz190tDxl %} ### 🪜 建立 Vue 3 + Vite 專案 ```bash npm create vite@latest ``` 接下來依照指示輸入: ✔ Project name: » vite-project(←你想要的專案名稱) ✔ Select a framework: » Vue ✔ Select a variant: » JavaScript ### 💿 接著進入資料夾 vite-project (←你想要的專案名稱)並安裝 ```bash cd vite-project ``` ### 📍 Vue 工具函式庫 與 Vite 插件行為檢查工具 ```bash npm i @vueuse/core npm i -D vite-plugin-inspect ``` 📌 用途說明 * `@vueuse/core`:給 Vue 組件內用的函式工具 * 常見像 useMouse()、useDark() 等實用函式,可以幫你快速綁定滑鼠座標、切換深色模式等互動功能。 * `vite-plugin-inspect`:前端開發用的 Vite 插件 ### 📍 初始化 Node 專案 ```bash npm init --yes npm i -D nodemon ``` ### 📍 安裝路由 > 無換頁就不需要 ```bash npm i vue-router ``` ### 📌 如果沒有的話需手動建立的檔案(.gitignore) #### 📝 建立檔案.gitignore > 內容為:要設定讓git忽略的檔案 檔名 ``` .gitignore ``` 內容(最少) ``` .env node_modules dump/* !dump/.gitkeep ``` ### 📍 安裝開發用工具(語法檢查、自動重啟) > 檢查語法是否符合規則(eslint / oslint / tslint…) > 檔案變動時自動重啟伺服器(開發用) ```bash npm init @eslint/config@latest ``` ```bash npm i -D eslint prettier eslint-plugin-prettier eslint-config-prettier eslint-plugin-vue ``` #### 📝 eslint.config.js 內容 ##### js 版 檔名 ``` eslint.config.js ``` 檔案內容(JS版) ```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.browser, ...globals.node } }, }, eslintPluginPrettierRecommended, { rules: { 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], }, }, ]) ``` ##### ts 版 檔名 ``` eslint.config.ts ``` 檔案內容(TS版) ```ts import js from '@eslint/js' import globals from 'globals' import pluginVue from 'eslint-plugin-vue' import tseslint from 'typescript-eslint' import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' import { defineConfig } from 'eslint/config' export default defineConfig( { ignores: ['dist', 'node_modules'] }, js.configs.recommended, tseslint.configs.recommended, ...pluginVue.configs['flat/recommended'], // 注意這裡需要展開運算子 { files: ['**/*.{js,mjs,cjs,ts,vue}'], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, rules: { // 自訂規則 '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], 'vue/multi-word-component-names': 'warn', 'vue/component-name-in-template-casing': ['error', 'PascalCase'], }, }, eslintPluginPrettierRecommended, ) ``` #### 📝 .prettierrc.json 內容 檔名 ``` .prettierrc.json ``` 檔案內容 ```json { "$schema": "https://json.schemastore.org/prettierrc", "printWidth": 100, "semi": false, "singleQuote": true } ``` --- > 選擇想要的UI框架,ex: [Nuxt UI](https://ui.nuxt.com/getting-started/installation/vue#install-the-nuxt-ui-package) 套件、[PrimeVue](https://primevue.org/) 套件、[daisyUI](https://daisyui.com/docs/install/) 套件 --- ### 🪜 安裝 UI 框架 #### NuxtUI ```bash npm i @nuxt/ui ``` ##### 設定 NuxtUI {%preview https://ui.nuxt.com/getting-started/installation/vue %} #### PrimeVue ```bash npm i primevue@latest primeicons primeflex ``` ##### 設定 PrimeVue {%preview https://primevue.org/vite %} #### DaisyUI ```bash npm install tailwindcss@latest @tailwindcss/vite@latest daisyui@latest ``` ##### 設定 DaisyUI {%preview https://daisyui.com/docs/install/vite/ %} … --- ### 🕶️ TailwindCSS 相關 #### 只想安裝 TailwindCSS ,不安裝其他 UI 框架 ```bash npm install tailwindcss @tailwindcss/vite ``` #### 在 vite.config 匯入 ```js import tailwindcss from '@tailwindcss/vite' ``` #### Tailwind 的設定 文件 {%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/SJMNh1Oqxe %} {%preview https://tailwindcss.com/docs/installation/using-vite %} ##### 在 vite.config 裡面的 plugins 中加入 ```js tailwindcss(), ``` ##### 在 src\style.css 匯入 Tailwind ```css @import "tailwindcss"; ``` ##### 檢查 main.js 中是否有匯入這個 css 檔案 ```js import './style.css' ``` --- ## 2️⃣ 常駐工具型功能(必裝輔助開發用,不是主流程但超常出現) 📦 定義:專案未必一開始就用到,但絕大多數專案後期一定會補,甚至常常預設就先裝。 ### 狀態管理、狀態儲存、 HTTP 請求、表單驗證 ```bash npm i pinia pinia-plugin-persistedstate axios vee-validate yup ``` ### 開發用套件 自動引入 import 的工具、不用手動 import 與註冊 Vue 元件、自動根據 /src/pages 資料夾產生 vue-router 路由表、多版型(多 Layout)」功能、page對應layout、自動打包字體 ```bash npm i -D unplugin-auto-import unplugin-vue-components unplugin-vue-router vite-plugin-vue-layouts-next unplugin-fonts ``` ### 在 /src 需手動建立的檔案建議(結構) ##### 新增 `src/頁面` 資料夾 > 放「主要頁面元件」(通常會被 router 導向) > 📌 通常一個頁面對應一個 .vue 元件(例如:首頁、會員頁、商品頁) 資料夾名稱 > 安裝 unplugin-vue-router 的話選這個 ``` pages ``` 或 ``` views ``` ``` index.vue ``` ```vue <template> <!-- index.vue --> </template> <script setup> // ... </script> ``` ##### 新增 `src/版型` 資料夾 > 定義整體版型(如:有無側欄、有無導覽列的框架) > 📌 常用於包住多個頁面,搭配 <router-view /> 使用 資料夾名稱 ``` layouts ``` ``` <template> <div class="min-h-screen"> <div> <!-- menu --> </div> <main> <!-- content page --> <router-view /> </main> </div> <div> <!-- footer --> </div> </template> <script setup> // ... </script> ``` ##### 新增 `src/plugins` 資料夾 > 放 plugin 設定檔(UI 套件、驗證套件等) > 📌 例如 PrimeVue、VeeValidate 的初始化設定、全域註冊等 資料夾名稱 ``` plugins ``` ##### 新增 `src/CSS樣式` 資料夾 > 放全域樣式檔、SCSS、變數設定 > 📌 若有用 SCSS、變數或 reset.css 都放這裡 資料夾名稱 ``` styles ``` ##### 新增 `src/放 useXXX()` 資料夾 > 自定義 Vue 邏輯的工具(使用 Composition API) > 📌 用來寫 useXXX() 形式的函式工具箱 資料夾名稱 ``` composables ``` ##### 新增 `src/工具箱` 資料夾 > 純 JavaScript 邏輯工具函式(無 Vue 依賴) > 📌 字串處理、格式化、日期運算等不牽涉狀態的通用工具 資料夾名稱 ``` utils ``` 或 ``` helpers ``` #### 有安裝套件類 ##### 新增 `src/路由` 資料夾 > 安裝 vue-router > 🚦 處理頁面之間的跳轉(SPA 路由系統) 資料夾名稱 ``` router ``` 基礎檔案 ``` index.js ``` ```js // src/router/index.js import { setupLayouts } from 'virtual:generated-layouts' import { createRouter, createWebHashHistory, START_LOCATION } from 'vue-router/auto' import { routes } from 'vue-router/auto-routes' import userService from '@/services/user' import { useUserStore } from '@/stores/user' // 🛠️ 第 2 步:建立 router 實例並套用 layout const router = createRouter({ history: createWebHashHistory(import.meta.env.BASE_URL), // 🧩 第 1 步:套用 Layout 與自動路由 routes: setupLayouts(routes), }) // 🛡️ 第 3 步:設定全域路由守衛 beforeEach(判斷登入狀態與頁面權限) router.beforeEach(async (to, from, next) => { const user = useUserStore() if (from === START_LOCATION && user.isLoggedIn) { try { const { data } = await userService.profile() user.login(data.user) } catch (error) { console.error(error) user.token = '' } } if (to.meta.login === 'no-login-only' && user.isLoggedIn) { next('/') } else if (to.meta.login === 'login-only' && !user.isLoggedIn) { next('/login') } else if (to.meta.admin && !user.isAdmin) { next('/') } else { next() } }) // 🏷️ 第 4 步:導航完成後自動設定 document.title router.afterEach(to => { document.title = `${to.meta.title} | 網站標題` }) // 🧯 第 5 步:onError 錯誤處理,避免 dynamic import 崩潰 router.onError((err, to) => { if (err?.message?.includes?.('Failed to fetch dynamically imported module')) { if (localStorage.getItem('router:dynamic-reload')) { console.error('Dynamic import error, reloading page did not fix it', err) } else { console.log('Reloading page to fix dynamic import error') localStorage.setItem('router:dynamic-reload', 'true') location.assign(to.fullPath) } } else { console.error(err) } }) // 🧹 第 6 步:router 初始化完成後清除錯誤 reload 記號 router.isReady().then(() => { localStorage.removeItem('router:dynamic-reload') }) export default router ``` ##### 新增 `src/連接後端或外部API` 資料夾 > 安裝 expess 或是 axios > 🔁 所有「跟後端互動」的動作都寫這裡(負責 axios 請求的封裝) 資料夾名稱 ``` services ``` ```js // src/services/api.js import axios from 'axios' import { useUserStore } from '@/stores/user' // 無須登入即可使用(接後端) const api = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:4000', }) // 須登入才能使用(接後端) const apiAuth = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:4000', }) // 每次發送請求前自動加上 token apiAuth.interceptors.request.use((config) => { const user = useUserStore() config.headers.Authorization = `Bearer ${user.token}` return config }) // token 相關 成功處理與失敗處理 apiAuth.interceptors.response.use( (res) => res, async (error) => { if ( error.response && error.response.status === 400 && error.response.data.message === 'token 已過期' && error.config.url !== '/user/refresh' ) { const user = useUserStore() try { // 直接使用 apiAuth instance 發送請求,避免循環相依 const { data } = await apiAuth.patch('/user/refresh') user.token = data.token error.config.headers.Authorization = `Bearer ${data.token}` return axios(error.config) } catch { user.logout() } } throw error }, ) export default { api, apiAuth } ``` ##### 新增 `src/鳳梨` 資料夾 > 安裝 pinia > 🧠 管理全域狀態(登入資訊、購物車、token 等),類似 Vuex 的進化版 資料夾名稱 ``` stores ``` ``` pinia ``` 檔案 ``` index.js ``` ``` // src/stores/index.js import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(piniaPluginPersistedstate) export default pinia ``` --- ### 日期處理 ```bash npm i dayjs ``` ### icon 額外擴充 {%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/SkLmkfOlWe %} ## 3️⃣ 明確功能型功能(安裝你需要用的功能套件,依需求增減) 📦 定義:為了「特定功能」才會加的套件,專案不同會差很多。 作業要用到 gsap、swiper、animejs ```bash npm install gsap swiper animejs ``` | 套件名 | 分類 | 用途 | 特點 | 適合情境 | | ------------ | ----- | ---------- | ------------------------------------ | ------------------ | | **GSAP** | 明確功能型 | 強大動畫函式庫 | 功能完整、控制精細、效能佳 | 頁面轉場、滾動動畫、複雜動畫流程 | | **Swiper** | 明確功能型 | 幻燈片輪播工具 | 手機觸控友善、支援 loop / autoplay / lazyload | Banner、圖片輪播、步驟流程切換 | | **Anime.js** | 明確功能型 | 動畫函式庫(較輕巧) | 語法簡潔、適合小動畫 | 元素浮動、icon 動畫、數字增減 | > **Swiper**有vue版,所以: > 📌 使用 Swiper 時需搭配 `swiper/vue` 元件匯入,並引入樣式 `swiper/css` --- ## 需增加的檔案內容 安裝完之後要思考: 1. vite.config 是否需掛載 2. main.js(或 .ts)是否需要匯入 ### vite-project\vite.config.js ``` vite.config.js ``` ```js import { fileURLToPath, URL } from 'node:url' import Vue from '@vitejs/plugin-vue' import { defineConfig } from 'vite' // --- Plugins --- import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { VueRouterAutoImports } from 'unplugin-vue-router' import VueRouter from 'unplugin-vue-router/vite' import Layouts from 'vite-plugin-vue-layouts-next' import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), VueRouter({ dts: './types/typed-router.d.ts', }), Vue(), Layouts({ defaultLayout: 'default', }), AutoImport({ imports: [ 'vue', VueRouterAutoImports, { pinia: ['defineStore', 'storeToRefs'], }, ], dts: 'src/auto-imports.d.ts', dirs: ['src/stores'], eslintrc: { enabled: true, }, vueTemplate: true, }), Components({ extensions: ['.vue'], include: [/\.vue$/, /\.vue\?vue/], dts: 'src/components.d.ts', }), ], optimizeDeps: { exclude: [ 'vue-router', 'unplugin-vue-router/runtime', 'unplugin-vue-router/data-loaders', 'unplugin-vue-router/data-loaders/basic', ], }, define: { 'process.env': {} }, resolve: { alias: { '@': fileURLToPath(new URL('src', import.meta.url)), }, extensions: ['.js', '.json', '.jsx', '.mjs', '.ts', '.tsx', '.vue'], }, server: { port: 3000, }, }) ``` ### vite-project\src\App.vue ``` App.vue ``` ```html <template> <div id="app"> <router-view /> </div> </template> <script setup> // ... </script> ``` ### vite-project\src\main.js ``` main.js ``` ```js import { createApp } from 'vue' import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import App from './App.vue' import router from './router' // 引入 Tailwind CSS 主樣式 import './style.css' const app = createApp(App) const pinia = createPinia() pinia.use(piniaPluginPersistedstate) app.use(pinia) app.use(router) app.mount('#app') ``` ```js import { createApp } from 'vue' import router from './router' import pinia from './stores' import './style.css' import App from './App.vue' // 創建應用程式 const app = createApp(App) // 安裝插件 app.use(pinia) app.use(router) // 掛載應用程式 app.mount('#app') ``` ### vite-project\.browserslistrc 定義要支援的瀏覽器版本(例如 last 2 versions, > 1%)。 Vite / Babel / PostCSS / Autoprefixer 會依這裡決定要轉譯的語法和加的 CSS 前綴。 ``` .browserslistrc ``` ``` > 1% last 2 versions not dead not ie 11 ``` ### vite-project\.editorconfig 統一編輯器的基本格式設定(縮排、換行符號、字元編碼等),確保團隊開發風格一致。 ``` .editorconfig ``` ``` [*.{js,jsx,ts,tsx,vue}] indent_style = space indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true ``` ### vite-project\.eslintrc-auto-import.json unplugin-auto-import 生成的檔案,列出自動匯入的變數/函式,讓 ESLint 知道它們是合法的全域變數,不會報「未定義」 ``` .eslintrc-auto-import.json ``` ### vite-project\auto-imports.d.ts unplugin-auto-import 自動生成,聲明自動匯入的函式/變數型別,讓 TypeScript 能正確提示與檢查。 ``` auto-imports.d.ts ``` ### vite-project\components.d.ts unplugin-vue-components 生成,聲明自動註冊的 Vue 元件型別,讓模板內使用時有 IntelliSense 提示。 ``` components.d.ts ``` ### vite-project\index.html Vite 的專案入口 HTML,會載入 JS 入口檔(如 main.js 或 main.ts) ``` index.html ``` ### vite-project\jsconfig.json 設定專案的路徑別名(例如 @ 指向 src),提供給 VSCode 與 TypeScript 的 IntelliSense 使用。 在純 JS 專案中等同 tsconfig.json 的路徑設定功能。 ``` jsconfig.json ``` ```json { "compilerOptions": { "allowJs": true, // 允許 JavaScript 檔案 "target": "ES2020", // 使用較新 JS 標準(支援 async/await) "module": "ESNext", // 使用原生模組 "baseUrl": ".", // 設定相對路徑基礎 "paths": { "@/*": ["src/*"] // 設定 @ 為 src 的別名 }, "moduleResolution": "bundler",// 配合 Vite bundler 行為 "jsx": "preserve", // 為 Vue JSX 保留 "types": ["vite/client"], // 支援 Vite 環境變數 "lib": ["ESNext", "DOM"] // 使用最新 JS + DOM 型別 }, "include": ["src/**/*", "types/**/*.d.ts"] } ``` ### vite-project\typed-router.d.ts 與 unplugin-vue-router 相關,提供型別安全的路由參數與路徑提示。 ``` typed-router.d.ts ``` --- # 各資料夾「建立時機」與啟用條件 | 資料夾 / 功能區 | 建立時機 or 什麼情況需要? | 備註 | | -------------- | ------------------------------------------- | --------------------------- | | `router/` | 安裝 `vue-router` 後(無論是手動還是自動路由) | 建立 `index.js` 並定義 routes | | `views/` | 一開始就需要,對應每個路由畫面 | 每個畫面一個 .vue | | `components/` | 實作畫面時會自然需要(按鈕、表單、卡片等) | 建議分類 + 對應 props/slots | | `plugins/` | 安裝任何 Plugin 後(如 PrimeVue, VeeValidate) | 把初始化邏輯集中收納 | | `store/` | 有跨元件 / 跨頁共用資料時(登入狀態、購物車等) | Pinia + plugin 註冊 | | `services/` | 只要開始呼叫 API,就應建立 | 每個功能模組一個 .js | | `composables/` | 出現多次共用邏輯時(如 useForm、useAuth、useQueryString) | 自定義 useXXX 函式區 | | `constants/` | 有固定選項、對照資料、狀態碼等時(如 select 選單) | 與邏輯分離,方便維護 | | `.env` | 有 baseURL、PORT、API key 等變數需求時 | `VITE_` 為前端變數前綴 | | `.gitignore` | 開專案第一天就該建立 | 避免上傳 node\_modules / .env 等 | # 🧩 前端套件分類表 | 套件名稱 | 安裝位置 | 類型 | 用途說明 | 代表常見用途 | | ---------------------- | ---- | ------- | ------------------------- | ----------- | | `vue` | ✅ 前端 | 主框架 | 建立 Vue App | 必裝主角 | | `vite` | ✅ 前端 | 開發工具 | 開發伺服器、熱更新 | 必裝主角 | | `@vitejs/plugin-vue` | ✅ 前端 | 插件 | 支援 `.vue` 組件語法 | Vite 必備 | | `vue-router` | ✅ 前端 | 路由工具 | 頁面切換、SPA | 路由系統 | | `pinia` | ✅ 前端 | 狀態管理 | Vue 3 官方推薦 state 工具 | 全域資料 | | `axios` | ✅ 前端 | HTTP 工具 | 前端請求 API | 抓資料 | | `vee-validate` / `yup` | ✅ 前端 | 表單驗證 | 表單輸入檢查 | 登入、註冊 | | `@iconify/vue` | ✅ 前端 | Icon | 通用 icon 套件 | 自定義圖示 | | `dayjs` | ✅ 前端 | 日期處理 | 處理時間與格式 | 預約、顯示日期 | | `gsap` / `animejs` | ✅ 前端 | 動畫 | 控制動畫流程 | 頁面轉場 | | `swiper` | ✅ 前端 | 輪播工具 | 輪播圖、滑動效果 | banner、步驟切換 | | `@vueuse/core` | ✅ 前端 | 工具函式庫 | 一堆超實用的 Vue composition 工具 | 類似小助手 | | `vite-plugin-inspect` | ✅ 前端 | Vite 插件 | 檢查套件掛載情況 | debug 開發流程 | ---