Vite 是什麼
Vite 是一個以 ES Modules 為基礎、具熱更新 (HMR) 的前端開發工具。開發階段直接讓瀏覽器載入原始碼,不需要預先打包。發佈階段再透過 Rollup 或 Rolldown 打包成靜態檔案。
Vite 指令與執行模式
1. 常用指令
| 指令 |
功能 |
預設模式 (mode) |
npm run dev 或 vite |
啟動開發伺服器,支援即時熱更新 (HMR) |
development |
npm run build 或 vite build |
打包專案成靜態檔案 |
production |
npm run preview 或 vite preview |
啟動一個本地伺服器預覽打包結果 |
production |
2. npm run dev vs npm run preview
| 項目 |
npm run dev |
npm run preview |
| 功能 |
啟動開發伺服器 (Vite Dev Server) |
啟動預覽伺服器 |
| 模式 |
development |
production |
| 資源載入 |
即時讀取原始碼 (src/main.ts) |
載入打包後的靜態檔 (dist/) |
| 是否需先打包 |
不需要 |
需要先執行 npm run build |
| 用途 |
開發、即時更新 (Hot Module Replacement, HMR) |
模擬正式部署 |
Vite 設定檔 vite.config.ts 基本結構
範例
1 2 3 4 5 6 7
| export default defineConfig({ plugins: [react()], server: { port: 3000, open: true } })
|
說明
defineConfig():Vite 官方提供的函式,提供型別提示與確保匯出的物件符合 Vite 設定格式。
plugins:Vite 本身只是一個通用的建構工具,並不「內建」React、Vue、Svelte 等框架支援。設定插件,例如 React,讓 Vite 知道要:
- 如何編譯
.jsx 或 .tsx 檔案。
- 如何處理 React HMR (Hot Module Replacement)。
- 如何解析 JSX 語法。
server.port:指定本地伺服器埠號。
server.open:啟動時自動開啟瀏覽器。
根據模式 mode 載入 .env 檔
範例
1 2 3 4 5 6 7 8 9 10 11
| export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), 'VITE') const port = parseInt(env.VITE_PORT ?? '5173', 10)
return { plugins: [react()], server: { port } } })
|
mode:決定要載入哪一組 .env.<mode> 檔。
process.cwd():回傳專案根目錄路徑。
'VITE':只載入以 VITE_ 開頭的變數。
parseInt(..., 10):將字串轉為整數 (10 表十進位)。若 .env 中沒有 VITE_PORT,parseInt(..., 10) 結果為 NaN,所以常加預設值。
mode 是什麼?從哪裡來?
當你執行 Vite 指令時,CLI 會呼叫一個 resolveConfig() 函式,在 resolveConfig() 裡面,Vite 會建立這個「上下文物件」,大致長這樣:
1 2 3 4 5 6 7
| { command: 'serve', mode: 'development', isSsrBuild: false, isPreview: false, ssrBuild: false }
|
再將這個物件,傳進你的設定函式裡:
1 2 3 4
| export default defineConfig(({ command, mode }) => { console.log(command) console.log(mode) })
|
指令對應到的 mode 類別
| 指令 |
預設 mode |
vite / vite dev |
development |
vite build |
production |
vite preview |
production |
可以透過 --mode 覆寫,此時 mode 會變成 'staging',並讀取 .env.staging:
1
| vite build --mode staging
|
.env 檔載入順序
Vite 根據 mode 依序載入 (後者會覆蓋前者):
.env
.env.local
.env.[mode]
.env.[mode].local
例:mode = 'development' 時載入順序:
1
| .env → .env.local → .env.development → .env.development.local
|
若該檔案不存在,接跳過載入該檔案,並繼續下一個檔案。.env 一直存在且一直都被載入。
process.cwd() 與 __dirname 差別
| 名稱 |
回傳 |
用途 |
process.cwd() |
當前執行 Vite 指令的資料夾 |
要根據專案根目錄找資源時 (Vite 常用) |
__dirname |
當前檔案所在的資料夾 |
要根據檔案位置操作檔案時 |
Vite 使用 process.cwd(),是為了保證即使設定檔在子資料夾,也能找到專案根目錄。
tsconfigPaths() 插件
1 2 3 4 5
| import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({ plugins: [react(), tsconfigPaths()] })
|
功能
自動讀取 tsconfig.json 裡的 paths 設定,讓 Vite 能識別 TypeScript 別名,例如:
1 2 3
| "paths": { "@components/*": ["src/components/*"] }
|
若不使用插件
需手動設定 resolve.alias:
1 2 3 4 5 6 7 8
| export default defineConfig({ plugins: [react(), tsconfigPaths()], resolve: { alias: { '@components': path.resolve(__dirname, 'src/components') } } })
|
base 與打包後資源路徑
base 用來控制 打包後資源路徑在 HTML 中的 URL 前綴。
開發與打包階段的 index.html
開發時專案目錄下的 index.html:
1 2 3 4
| <body> <div id="root"></div> <script type="module" src="/src/main.tsx"></script> </body>
|
打包後結構:
1 2 3 4 5 6
| project/ ├─ dist/ │ ├─ assets/ │ │ ├─ index-COcDBgFa.css │ │ └─ index-DLt9nNv6.js │ └─ index.html
|
base: '/',打包後的 index.html:
1 2 3 4 5 6 7 8
| <head> <title>first-vite</title> <script type="module" crossorigin src="/assets/index-DLt9nNv6.js"></script> <link rel="stylesheet" crossorigin href="/assets/index-COcDBgFa.css"> </head> <body> <div id="root"></div> </body>
|
base: '/myapp/',打包後的 index.html:
1 2 3 4 5 6 7 8
| <head> <title>first-vite</title> <script type="module" crossorigin src="/myapp/assets/index-DLt9nNv6.js"></script> <link rel="stylesheet" crossorigin href="/myapp/assets/index-COcDBgFa.css"> </head> <body> <div id="root"></div> </body>
|
用來部屬在伺服器的子資料夾 myapp/ 裡面 (例如 GitHub Pages 或 Nginx 子站),需要將整個 dist/ 的內容放入該子資料夾中:
1 2 3 4 5 6
| (伺服器根目錄) └── myapp/ ├── assets/ │ ├── index-COcDBgFa.css │ └── DLt9nNv6.js └─ index.html
|
base: '',打包後的 index.html:
1 2 3 4 5 6 7 8
| <head> <title>first-vite</title> <script type="module" crossorigin src="assets/index-DLt9nNv6.js"></script> <link rel="stylesheet" crossorigin href="assets/index-COcDBgFa.css"> </head> <body> <div id="root"></div> </body>
|
1 2 3 4 5 6 7 8
| <head> <title>first-vite</title> <script type="module" crossorigin src="https://cdn.example.com/assets/index-DLt9nNv6.js"></script> <link rel="stylesheet" crossorigin href="https://cdn.example.com/assets/index-COcDBgFa.css"> </head> <body> <div id="root"></div> </body>
|
總結
| base |
打包後資源路徑 |
用途 |
'/' |
/assets/... |
部署在根目錄。絕對路徑,從網站根目錄載入。 |
'/myapp/' |
/myapp/assets/... |
部署在子資料夾。絕對路徑,從子目錄載入。 |
'' |
assets/... |
相對路徑,根據 HTML 位置載入 |
'https://cdn.example.com/' |
https://cdn.example.com/assets/... |
CDN 載入 |
注意
若設定:
1 2
| base: 'https://cdn.example.com/assets/', build: { assetsDir: 'assets' }
|
打包結果會變成:
1
| https://cdn.example.com/assets/assets/...
|
正確寫法:
1 2 3 4 5 6
| base: 'https://cdn.example.com/'
base: 'https://cdn.example.com/assets/', build: { assetsDir: '' }
|
完整設定範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), 'VITE') const port = parseInt(env.VITE_PORT, 10)
return { plugins: [react(), tsconfigPaths()], base: '', server: { port: !Number.isNaN(port) ? port : 5001, }, test: { environment: 'jsdom', globals: true, setupFiles: './src/__tests__/setup.ts', outputFile: { junit: './project-client.junit.xml', json: './project-client.report.json', }, maxWorkers: 4, minWorkers: 4, }, } })
|
重點整理:
| 主題 |
說明 |
mode |
由 CLI 傳入 (development / production),可用 --mode 改變 |
loadEnv() |
根據 mode 載入 .env 檔 |
process.cwd() |
回傳專案根目錄 |
tsconfigPaths() |
讓 Vite 支援 TypeScript 別名 |
base |
打包後資源的 URL 前綴 |
build.outDir |
打包輸出資料夾 |
test |
Vitest 的設定 |
npm run dev |
即時開發、HMR 更新 |
npm run preview |
模擬正式部署結果 |