Vite 模式¶
Vite 8+ 项目的构建工具及开发服务器模式。涵盖配置、环境变量、代理设置、库模式、依赖项预打包以及常见的生产环境陷阱。
何时使用¶
- 配置
vite.config.ts或vite.config.js时 - 配置环境变量或
.env文件时 - 为 API 后端配置开发服务器代理时
- 优化构建输出(代码块、压缩、资源)时
- 使用
build.lib发布库时 - 排查依赖项预打包或 CJS/ESM 互操作性问题时
- 调试 HMR、开发服务器或构建错误时
- 选择或排序 Vite 插件时
工作原理¶
- 开发模式以原生 ESM 形式提供源文件(无打包)。转换按模块请求按需进行,因此冷启动快且 HMR 精确。
- 构建模式使用 Rolldown(v7+)或 Rollup(v5 至 v6),通过树摇、代码分割和基于 Oxc 的压缩将应用打包为生产版本。
- 依赖项预打包通过 esbuild 将 CJS/UMD 依赖项一次性转换为 ESM,并将结果缓存到
node_modules/.vite。后续启动可跳过处理。 - 插件在开发和构建之间共享统一接口。同一个插件对象既可用于开发服务器的按需转换,也可用于生产流水线。
- 环境变量在构建时静态内联。带
VITE_前缀的变量成为包中的公共常量,无前缀的变量对客户端代码不可见。
示例¶
配置结构¶
基本配置¶
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': new URL('./src', import.meta.url).pathname },
},
})
条件配置¶
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd()) // VITE_ プレフィックスのみ(安全)
return {
plugins: [react()],
server: command === 'serve' ? { port: 3000 } : undefined,
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})
主要配置选项¶
| 键 | 默认值 | 说明 |
|---|---|---|
root |
'.' |
项目根目录(index.html 的位置) |
base |
'/' |
部署资源的公共基础路径 |
envPrefix |
'VITE_' |
暴露给客户端的环境变量前缀 |
build.outDir |
'dist' |
输出目录 |
build.minify |
'oxc' |
压缩器('oxc'、'terser' 或 false) |
build.sourcemap |
false |
true、'inline' 或 'hidden' |
插件¶
必备插件¶
大多数插件需求可由少数维护良好的包覆盖。在创建自定义插件之前,请先考虑这些包。
| 插件 | 目的 | 使用时机 |
|---|---|---|
@vitejs/plugin-react-swc |
通过 SWC 实现 React HMR + Fast Refresh | React 应用的默认选择(比 Babel 变体更快) |
@vitejs/plugin-react |
通过 Babel 实现 React HMR + Fast Refresh | 仅在需要 Babel 插件时使用(emotion、MobX 装饰器) |
@vitejs/plugin-vue |
Vue 3 SFC 支持 | Vue 应用 |
vite-plugin-checker |
在工作线程中运行带 HMR 覆盖层的 tsc + ESLint |
所有 TypeScript 应用 — Vite 在 vite build 期间不进行类型检查 |
vite-tsconfig-paths |
遵循 tsconfig.json 中的 paths 别名 |
tsconfig.json 中已有别名时 |
vite-plugin-dts |
在库模式下输出 .d.ts 文件 |
发布 TypeScript 库时 |
vite-plugin-svgr |
将 SVG 作为 React 组件导入 | 将 SVG 用作组件的 React 应用 |
rollup-plugin-visualizer |
包的树状图/旭日图报告 | 定期审计包大小(使用 enforce: 'post') |
vite-plugin-pwa |
零配置 PWA + Workbox | 离线应用 |
重要提示: vite build 会转译,但不会进行类型检查。除非添加 vite-plugin-checker 或在 CI 中运行 tsc --noEmit,否则类型错误会静默地发布到生产环境。
创建自定义插件¶
创建自定义插件的情况很少。大多数需求可由现有插件覆盖。如确有需要,请先在 vite.config.ts 中以内联方式编写,仅在需要复用时再抽取。
// vite.config.ts — 最小限のインラインプラグイン
function myPlugin(): Plugin {
return {
name: 'my-plugin', // 必須、一意でなければならない
enforce: 'pre', // 'pre' | 'post'(オプション)
apply: 'build', // 'build' | 'serve'(オプション)
transform(code, id) {
if (!id.endsWith('.custom')) return
return { code: transformCustom(code), map: null }
},
}
}
主要钩子: transform(修改源码)、resolveId + load(虚拟模块)、transformIndexHtml(注入 HTML)、configureServer(添加开发中间件)、hotUpdate(自定义 HMR — 替代 v7+ 中已弃用的 handleHotUpdate)。
虚拟模块使用 \0 前缀约定 — resolveId 返回 '\0virtual:my-id' 可让其他插件跳过。用户代码导入 'virtual:my-id'。
完整插件 API 请参阅 vite.dev/guide/api-plugin。调试开发中的转换流水线请使用 vite-plugin-inspect。
HMR API¶
框架插件(如 @vitejs/plugin-react、@vitejs/plugin-vue)会自动处理 HMR。仅在构建自定义状态存储、开发工具或需要跨更新保持状态的框架无关实用工具时,才直接使用 import.meta.hot。
// src/store.ts — バニラモジュールの手動HMR
if (import.meta.hot) {
// 更新を跨いで状態を保持する(.dataを再代入せず、必ず変更すること)
import.meta.hot.data.count = import.meta.hot.data.count ?? 0
// モジュールが置き換えられる前にサイドエフェクトをクリーンアップ
import.meta.hot.dispose((data) => clearInterval(data.intervalId))
// このモジュール自身の更新を受け入れる
import.meta.hot.accept()
}
所有 import.meta.hot 代码都会从生产构建中树摇移除 — 无需删除守卫。
环境变量¶
Vite 按顺序读取 .env、.env.local、.env.[mode]、.env.[mode].local(后者覆盖前者)。*.local 文件被 gitignore,用于本地机密。
客户端访问¶
仅带 VITE_ 前缀的变量会暴露给客户端代码:
import.meta.env.VITE_API_URL // string
import.meta.env.MODE // 'development' | 'production' | カスタム
import.meta.env.BASE_URL // base設定値
import.meta.env.DEV // boolean
import.meta.env.PROD // boolean
import.meta.env.SSR // boolean
在配置中使用环境变量¶
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd()) // VITE_ プレフィックスのみ(安全)
return {
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})
安全¶
VITE_ 前缀不是安全边界¶
带 VITE_ 前缀的变量在构建时静态内联到客户端包中。压缩、base64 编码或禁用源码映射都无法隐藏它们。恶意攻击者可以从发布的 JavaScript 中提取任意 VITE_ 变量。
规则: 仅将公共值(API URL、功能开关、公共密钥)放入 VITE_ 变量。机密(API 令牌、数据库 URL、私钥)必须放在 API 或无服务器函数背后的服务端。
loadEnv('') 陷阱¶
// BAD: 第3引数として '' を渡すと、サーバーのシークレットを含む全ての環境変数が読み込まれ、
// `define` でクライアントコードにインライン化できてしまう。
const env = loadEnv(mode, process.cwd(), '')
// GOOD: 明示的なプレフィックスリスト
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])
生产环境源映射¶
生产环境的源映射会泄露原始源代码。除非上传到错误追踪器(Sentry、Bugsnag)并在本地删除,否则请禁用:
.gitignore 检查清单¶
.env.local、.env.*.local— 本地密钥覆盖dist/— 构建输出node_modules/.vite— 预打包缓存(旧条目会导致幽灵错误)
服务器代理¶
// vite.config.ts — server.proxy
server: {
proxy: {
'/foo': 'http://localhost:4567', // 文字列の短縮形
'/api': {
target: 'http://localhost:8080',
changeOrigin: true, // 仮想ホストバックエンドに必要
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
}
对于 WebSocket 代理,请在路由配置中添加 ws: true。
构建优化¶
手动分块¶
// vite.config.ts — build.rolldownOptions
build: {
rolldownOptions: {
output: {
// オブジェクト形式:特定のパッケージをグループ化
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-popover'],
},
},
},
}
// 関数形式:ヒューリスティックで分割
manualChunks(id) {
if (id.includes('node_modules/react')) return 'react-vendor'
if (id.includes('node_modules')) return 'vendor'
}
性能¶
避免桶文件¶
桶文件(从目录重新导出所有内容的 index.ts)会强制加载所有被重新导出的文件,即使只导入一个符号。这是官方文档中提到的开发服务器速度下降的主要原因。
// BAD — 1つのユーティリティのインポートがViteにバレル全体を読み込ませる
import { slash } from '@/utils'
// GOOD — 直接インポート、そのファイルだけが読み込まれる
import { slash } from '@/utils/slash'
显式导入扩展名¶
隐式扩展名会通过 resolve.extensions 强制最多 6 次文件系统检查。在大型代码库中会累积。
请将 tsconfig.json 中的 allowImportingTsExtensions 和 resolve.extensions 限制为实际使用的扩展名。
热路由预热¶
server.warmup.clientFiles 会在浏览器请求之前预转换已知的热入口。这可以消除大型应用冷加载请求的瀑布效应。
// vite.config.ts
server: {
warmup: {
clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
},
}
分析缓慢的开发服务器¶
如果感觉 vite dev 很慢,请从 vite --profile 开始,操作应用,然后按 p+enter 保存 .cpuprofile。在 Speedscope 中加载,查看哪个插件消耗了时间(通常是社区插件的 buildStart、config 或 configResolved 钩子)。
库模式¶
如果发布 npm 包,请使用 build.lib。有两个比配置细节更重要的陷阱:
- 类型不会被输出 — 请添加
vite-plugin-dts,或单独运行tsc --emitDeclarationOnly。 - 必须外部化对等依赖 — 如果未列出的对等依赖被打包进库中,会导致使用者出现重复运行时错误。
// vite.config.ts
build: {
lib: {
entry: 'src/index.ts',
formats: ['es', 'cjs'],
fileName: (format) => `my-lib.${format}.js`,
},
rolldownOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'], // すべてのピア依存関係
},
}
SSR 外部化¶
裸的 createServer({ middlewareMode: true }) 设置面向框架作者。大多数应用应使用 Nuxt、Remix、SvelteKit、Astro 或 TanStack Start。作为框架用户,需要调整的是依赖项在 SSR 中损坏时的外部化配置:
// vite.config.ts — SSRオプション
ssr: {
external: ['node-native-package'], // SSRバンドルで require() として保持
noExternal: ['esm-only-package'], // SSR出力に強制バンドル(ほとんどのSSRエラーを修正)
target: 'node', // 'node' または 'webworker'
}
依赖项预打包¶
Vite 会预打包依赖项,将 CJS/UMD 转换为 ESM,并减少请求数量。
// vite.config.ts — optimizeDeps
optimizeDeps: {
include: [
'lodash-es', // 重い依存関係を強制的に事前バンドル
'cjs-package', // 相互運用問題を引き起こすCJS依存関係
'deep-lib/components/**', // 深いインポートのグロブ
],
exclude: ['local-esm-package'], // 除外する場合は有効なESMでなければならない
force: true, // キャッシュを無視して再最適化(一時的なデバッグ)
}
常见陷阱¶
开发与构建不一致¶
开发使用 esbuild/Rolldown 进行转换,构建使用 Rolldown 进行打包。CJS 库在两者中可能表现不同。部署前请务必使用 vite build && vite preview 验证。
部署后的旧块¶
新构建会生成新的块哈希。拥有活跃会话的用户会请求已不存在的旧文件名。Vite 没有内置解决方案。缓解措施:
- 在部署窗口期间保留旧的
dist/assets/文件 - 在路由器中捕获动态导入错误并重新加载页面
Docker 与容器¶
Vite 默认绑定到 localhost,容器外部无法访问:
// vite.config.ts — Docker/コンテナ設定
server: {
host: true, // 0.0.0.0 にバインド
hmr: { clientPort: 3000 }, // リバースプロキシ経由の場合
}
单体仓库文件访问¶
Vite 限制向项目根目录提供文件。根目录外的包会被阻止:
反模式¶
// BAD: envPrefix を '' にすると全ての環境変数(シークレットを含む)がクライアントに公開される
envPrefix: ''
// BAD: アプリケーションソースコードで require() が動くと思い込む — ViteはESMファースト
const lib = require('some-lib') // 代わりに import を使用
// BAD: 全てのnode_moduleを個別のチャンクに分割する — 何百もの小さなファイルを生成
manualChunks(id) {
if (id.includes('node_modules')) {
return id.split('node_modules/')[1].split('/')[0] // パッケージごとに1チャンク
}
}
// BAD: ライブラリモードでピア依存関係を外部化しない — 重複ランタイムエラーを引き起こす
// rolldownOptions.external なしの build.lib
// BAD: 非推奨のesbuildミニファイアーを使用する
build: { minify: 'esbuild' } // 'oxc'(デフォルト)または 'terser' を使用
// BAD: import.meta.hot.data を再代入で変更する
import.meta.hot.data = { count: 0 } // 誤り:プロパティを変更すべきで再代入しない
import.meta.hot.data.count = 0 // 正しい
流程反模式:
vite preview不是生产服务器 — 它是已构建包的冒烟测试。请将dist/部署到实际静态主机(NGINX、Cloudflare Pages、Vercel 静态),或使用多阶段 Dockerfile。- 期望
vite build执行类型检查 — 它只做转译。类型错误会被静默发布到生产环境。请添加vite-plugin-checker,或在 CI 中运行tsc --noEmit。 - 默认引入
@vitejs/plugin-legacy— 会使包体积膨胀约 40%,破坏源映射的包分析器,并且对 95% 以上的现代浏览器用户没有必要。请基于实际分析数据而非假设来应用。 - 使用 30 多个重复的
resolve.alias条目手动管理tsconfig.json路径 — 请改用vite-tsconfig-paths。由于在 Excalidraw 和 PostHog 中观察到此问题,新项目应避免这样做。 - 依赖项变更后遗留旧的
node_modules/.vite— 预打包缓存会导致幽灵错误。请在切换分支或修补依赖项后清除。
快速参考¶
| 模式 | 使用时机 |
|---|---|
defineConfig |
始终 — 提供类型推断 |
loadEnv(mode, root, ['VITE_']) |
在配置中访问环境变量(显式前缀) |
vite-plugin-checker |
TypeScript 应用(弥补类型检查缺口) |
vite-tsconfig-paths |
作为手动 resolve.alias 的替代 |
optimizeDeps.include |
引发互操作问题的 CJS 依赖 |
server.proxy |
开发期间将 API 请求路由到后端 |
server.host: true |
Docker、容器、远程访问 |
server.warmup.clientFiles |
热路径的预转换 |
build.lib + external |
发布 npm 包 |
manualChunks(对象形式) |
拆分 vendor 包 |
vite --profile |
调试缓慢的开发服务器 |
vite build && vite preview |
本地冒烟测试生产包(不是生产服务器) |
相关技能¶
frontend-patterns— React 组件模式docker-patterns— 使用 Vite 的容器化开发nextjs-turbopack— Next.js 的替代打包器
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC