vite-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVite Patterns
Vite 模式
Build tool and dev server patterns for Vite 8+ projects. Covers configuration, environment variables, proxy setup, library mode, dependency pre-bundling, and common production pitfalls.
适用于Vite 8+项目的构建工具与开发服务器模式,涵盖配置、环境变量、代理设置、库模式、依赖预打包以及常见生产环境陷阱。
When to Use
适用场景
- Configuring or
vite.config.tsvite.config.js - Setting up environment variables or files
.env - Configuring dev server proxy for API backends
- Optimizing build output (chunks, minification, assets)
- Publishing libraries with
build.lib - Troubleshooting dependency pre-bundling or CJS/ESM interop
- Debugging HMR, dev server, or build errors
- Choosing or ordering Vite plugins
- 配置 或
vite.config.tsvite.config.js - 设置环境变量或 文件
.env - 为API后端配置开发服务器代理
- 优化构建输出(代码分割、压缩、资源处理)
- 通过 发布库
build.lib - 排查依赖预打包或CJS/ESM互操作性问题
- 调试HMR、开发服务器或构建错误
- 选择或排序Vite插件
How It Works
工作原理
- Dev mode serves source files as native ESM — no bundling. Transforms happen on-demand per module request, which is why cold starts are fast and HMR is precise.
- Build mode uses Rolldown (v7+) or Rollup (v5–v6) to bundle the app for production with tree-shaking, code-splitting, and Oxc-based minification.
- Dependency pre-bundling converts CJS/UMD deps to ESM once via esbuild and caches the result under , so subsequent starts skip the work.
node_modules/.vite - Plugins share a unified interface across dev and build — the same plugin object works for both the dev server's on-demand transforms and the production pipeline.
- Environment variables are statically inlined at build time. -prefixed vars become public constants in the bundle; everything unprefixed is invisible to client code.
VITE_
- 开发模式 以原生ESM形式提供源文件——无需打包。转换操作按需针对每个模块请求进行,这也是冷启动速度快、HMR精准的原因。
- 构建模式 使用Rolldown(v7+)或Rollup(v5–v6)为生产环境打包应用,支持摇树优化、代码分割和基于Oxc的压缩。
- 依赖预打包 通过esbuild将CJS/UMD依赖转换为ESM格式,并将结果缓存到 下,后续启动时可跳过该步骤。
node_modules/.vite - 插件 在开发和构建阶段共享统一接口——同一个插件对象可用于开发服务器的按需转换和生产流水线。
- 环境变量 在构建时静态内联。前缀为 的变量会成为包中的公共常量;无前缀的变量对客户端代码不可见。
VITE_
Examples
示例
Config Structure
配置结构
Basic Config
基础配置
typescript
// 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 },
},
})typescript
// 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 },
},
})Conditional Config
条件配置
typescript
// 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_ prefixed only (safe)
return {
plugins: [react()],
server: command === 'serve' ? { port: 3000 } : undefined,
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})typescript
// 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),
},
}
})Key Config Options
核心配置选项
| Key | Default | Description |
|---|---|---|
| | Project root (where |
| | Public base path for deployed assets |
| | Prefix for client-exposed env vars |
| | Output directory |
| | Minifier ( |
| | |
| 键名 | 默认值 | 描述 |
|---|---|---|
| | 项目根目录( |
| | 部署资源的公共基础路径 |
| | 客户端可访问环境变量的前缀 |
| | 输出目录 |
| | 压缩工具( |
| | 可选值 |
Plugins
插件
Essential Plugins
必备插件
Most plugin needs are covered by a handful of well-maintained packages. Reach for these before writing your own.
| Plugin | Purpose | When to use |
|---|---|---|
| React HMR + Fast Refresh via SWC | Default for React apps (faster than Babel variant) |
| React HMR + Fast Refresh via Babel | Only if you need Babel plugins (emotion, MobX decorators) |
| Vue 3 SFC support | Vue apps |
| Runs | Any TypeScript app — Vite does NOT type-check during |
| Honors | Any time you already have aliases in |
| Emits | Publishing TypeScript libraries |
| Imports SVGs as React components | React apps using SVGs as components |
| Bundle treemap/sunburst report | Periodic bundle size audits (use |
| Zero-config PWA + Workbox | Offline-capable apps |
Critical callout: transpiles but does NOT type-check. Type errors silently ship to production unless you add or run in CI.
vite buildvite-plugin-checkertsc --noEmit大多数插件需求都可通过少数维护良好的包满足,优先使用这些插件而非自行编写。
| 插件 | 用途 | 适用场景 |
|---|---|---|
| 通过SWC实现React HMR + Fast Refresh | React应用默认选择(比Babel版本更快) |
| 通过Babel实现React HMR + Fast Refresh | 仅当需要Babel插件(如emotion、MobX装饰器)时使用 |
| Vue 3单文件组件支持 | Vue应用 |
| 在工作线程中运行 | 所有TypeScript应用——Vite在 |
| 遵循 | 当你已在 |
| 在库模式下生成 | 发布TypeScript库 |
| 将SVG作为React组件导入 | 使用SVG作为组件的React应用 |
| 生成包的树形图/旭日图报告 | 定期进行包大小审计(需设置 |
| 零配置PWA + Workbox | 支持离线访问的应用 |
重要提示: 仅进行转译,不做类型检查。除非添加 或在CI中运行 ,否则类型错误会被静默地带到生产环境。
vite buildvite-plugin-checkertsc --noEmitAuthoring Custom Plugins
自定义插件开发
Authoring is rare — most needs are covered by existing plugins. When you do need one, start inline in and only extract if reused.
vite.config.tstypescript
// vite.config.ts — minimal inline plugin
function myPlugin(): Plugin {
return {
name: 'my-plugin', // required, must be unique
enforce: 'pre', // 'pre' | 'post' (optional)
apply: 'build', // 'build' | 'serve' (optional)
transform(code, id) {
if (!id.endsWith('.custom')) return
return { code: transformCustom(code), map: null }
},
}
}Key hooks: (modify source), + (virtual modules), (inject into HTML), (add dev middleware), (custom HMR — replaces deprecated in v7+).
transformresolveIdloadtransformIndexHtmlconfigureServerhotUpdatehandleHotUpdateVirtual modules use the prefix convention — returns so other plugins skip it. User code imports .
\0resolveId'\0virtual:my-id''virtual:my-id'For full plugin API, see vite.dev/guide/api-plugin. Use during development to debug the transform pipeline.
vite-plugin-inspect自定义插件开发并不常见——大多数需求已有现成插件覆盖。当确实需要时,先在 中内联编写,仅在需要复用再提取为独立插件。
vite.config.tstypescript
// 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 }
},
}
}核心钩子: (修改源码)、 + (虚拟模块)、(注入HTML)、(添加开发中间件)、(自定义HMR——替代v7+中已弃用的 )。
transformresolveIdloadtransformIndexHtmlconfigureServerhotUpdatehandleHotUpdate虚拟模块 使用 前缀约定—— 返回 ,以便其他插件跳过处理。用户代码通过 导入。
\0resolveId'\0virtual:my-id''virtual:my-id'完整插件API请参考 vite.dev/guide/api-plugin。开发期间可使用 调试转换流程。
vite-plugin-inspectHMR API
HMR API
Framework plugins (, , etc.) handle HMR automatically. Reach for directly only when building custom state stores, dev tools, or framework-agnostic utilities that need to persist state across updates.
@vitejs/plugin-react@vitejs/plugin-vueimport.meta.hottypescript
// src/store.ts — manual HMR for a vanilla module
if (import.meta.hot) {
// Persist state across updates (must MUTATE, never reassign .data)
import.meta.hot.data.count = import.meta.hot.data.count ?? 0
// Cleanup side effects before module is replaced
import.meta.hot.dispose((data) => clearInterval(data.intervalId))
// Accept this module's own updates
import.meta.hot.accept()
}All code is tree-shaken out of production builds — no guard removal needed.
import.meta.hot框架插件(、 等)会自动处理HMR。仅在构建自定义状态存储、开发工具或跨框架工具(需要在更新时保留状态)时,才直接使用 。
@vitejs/plugin-react@vitejs/plugin-vueimport.meta.hottypescript
// 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.hotEnvironment Variables
环境变量
Vite loads , , , and in that order (later overrides earlier); files are gitignored and meant for local secrets.
.env.env.local.env.[mode].env.[mode].local*.localVite按以下顺序加载 、、 和 (后续文件覆盖前面的); 文件会被git忽略,用于存储本地密钥。
.env.env.local.env.[mode].env.[mode].local*.localClient-Side Access
客户端访问
Only -prefixed vars are exposed to client code:
VITE_typescript
import.meta.env.VITE_API_URL // string
import.meta.env.MODE // 'development' | 'production' | custom
import.meta.env.BASE_URL // base config value
import.meta.env.DEV // boolean
import.meta.env.PROD // boolean
import.meta.env.SSR // boolean只有前缀为 的变量可被客户端代码访问:
VITE_typescript
import.meta.env.VITE_API_URL // 字符串类型
import.meta.env.MODE // 可选值 'development' | 'production' | 自定义模式
import.meta.env.BASE_URL // base配置值
import.meta.env.DEV // 布尔类型
import.meta.env.PROD // 布尔类型
import.meta.env.SSR // 布尔类型Using Env in Config
在配置中使用环境变量
typescript
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd()) // VITE_ prefixed only (safe)
return {
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})typescript
// 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),
},
}
})Security
安全注意事项
VITE_
Prefix is NOT a Security Boundary
VITE_VITE_
前缀并非安全边界
VITE_Any variable prefixed with is statically inlined into the client bundle at build time. Minification, base64 encoding, and disabling source maps do NOT hide it. A determined attacker can extract any var from the shipped JavaScript.
VITE_VITE_Rule: Only public values (API URLs, feature flags, public keys) go in vars. Secrets (API tokens, database URLs, private keys) MUST live server-side behind an API or serverless function.
VITE_任何前缀为 的变量都会在构建时静态内联到客户端包中。压缩、Base64编码和禁用Source Map都无法隐藏这些变量。有决心的攻击者可以从发布的JavaScript中提取任何 变量。
VITE_VITE_规则: 只有公共值(API地址、功能开关、公钥)可放入 变量。密钥(API令牌、数据库地址、私钥)必须放在服务器端,通过API或无服务器函数提供。
VITE_The loadEnv('')
Trap
loadEnv('')loadEnv('')
陷阱
loadEnv('')typescript
// BAD: passing '' as the third arg loads ALL env vars — including server secrets —
// and makes them available to inline into client code via `define`.
const env = loadEnv(mode, process.cwd(), '')
// GOOD: explicit prefix list
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])typescript
// 错误:传入''作为第三个参数会加载所有环境变量——包括服务器密钥——
// 并可通过`define`内联到客户端代码中。
const env = loadEnv(mode, process.cwd(), '')
// 正确:明确指定前缀列表
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])Source Maps in Production
生产环境中的Source Map
Production source maps leak your original source code. Disable them unless you upload to an error tracker (Sentry, Bugsnag) and delete locally afterward:
typescript
build: {
sourcemap: false, // default — keep it this way
}生产环境的Source Map会泄露原始源码。除非要上传到错误追踪工具(如Sentry、Bugsnag)并在之后本地删除,否则应禁用:
typescript
build: {
sourcemap: false, // 默认值——保持此设置
}.gitignore
Checklist
.gitignore.gitignore
检查清单
.gitignore- ,
.env.local— local secret overrides.env.*.local - — build output
dist/ - — pre-bundle cache (stale entries cause phantom errors)
node_modules/.vite
- ,
.env.local— 本地密钥覆盖文件.env.*.local - — 构建输出
dist/ - — 预打包缓存(过期条目会导致莫名错误)
node_modules/.vite
Server Proxy
服务器代理
typescript
// vite.config.ts — server.proxy
server: {
proxy: {
'/foo': 'http://localhost:4567', // string shorthand
'/api': {
target: 'http://localhost:8080',
changeOrigin: true, // needed for virtual-hosted backends
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
}For WebSocket proxying, add to the route config.
ws: truetypescript
// 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: trueBuild Optimization
构建优化
Manual Chunks
手动代码分割
typescript
// vite.config.ts — build.rolldownOptions
build: {
rolldownOptions: {
output: {
// Object form: group specific packages
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-popover'],
},
},
},
}typescript
// Function form: split by heuristic
manualChunks(id) {
if (id.includes('node_modules/react')) return 'react-vendor'
if (id.includes('node_modules')) return 'vendor'
}typescript
// vite.config.ts — build.rolldownOptions
build: {
rolldownOptions: {
output: {
// 对象形式:分组特定包
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-popover'],
},
},
},
}typescript
// 函数形式:按规则分割
manualChunks(id) {
if (id.includes('node_modules/react')) return 'react-vendor'
if (id.includes('node_modules')) return 'vendor'
}Performance
性能优化
Avoid Barrel Files
避免桶文件
Barrel files ( re-exporting everything from a directory) force Vite to load every re-exported file even when you import a single symbol. This is the #1 dev-server slowdown flagged by the official docs.
index.tstypescript
// BAD — importing one util forces Vite to load the whole barrel
import { slash } from '@/utils'
// GOOD — direct import, only the one file is loaded
import { slash } from '@/utils/slash'桶文件( 重新导出目录下所有内容)会迫使Vite加载所有被导出的文件,即使你只导入单个符号。这是官方文档指出的开发服务器变慢的头号原因。
index.tstypescript
// 错误——导入一个工具会迫使Vite加载整个桶文件
import { slash } from '@/utils'
// 正确——直接导入,仅加载单个文件
import { slash } from '@/utils/slash'Be Explicit with Import Extensions
明确导入扩展名
Each implicit extension forces up to 6 filesystem checks via . In large codebases, this adds up.
resolve.extensionstypescript
// BAD
import Component from './Component'
// GOOD
import Component from './Component.tsx'Narrow + to only the extensions you actually use.
tsconfig.jsonallowImportingTsExtensionsresolve.extensions每个隐式扩展名会触发最多6次文件系统检查(通过 )。在大型代码库中,这会累积成显著的开销。
resolve.extensionstypescript
// 错误
import Component from './Component'
// 正确
import Component from './Component.tsx'在 中缩小 + 的范围,仅保留实际使用的扩展名。
tsconfig.jsonallowImportingTsExtensionsresolve.extensionsWarm-Up Hot-Path Routes
预热热点路由
server.warmup.clientFilestypescript
// vite.config.ts
server: {
warmup: {
clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
},
}server.warmup.clientFilestypescript
// vite.config.ts
server: {
warmup: {
clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
},
}Profiling Slow Dev Servers
排查缓慢的开发服务器
When feels slow, start with , interact with the app, then press to save a . Load it in Speedscope to find which plugins are eating time — usually , , or hooks in community plugins.
vite devvite --profilep+enter.cpuprofilebuildStartconfigconfigResolved当 运行缓慢时,先执行 ,与应用交互,然后按 保存 文件。在 Speedscope 中加载该文件,找出耗时的插件——通常是社区插件中的 、 或 钩子。
vite devvite --profilep+enter.cpuprofilebuildStartconfigconfigResolvedLibrary Mode
库模式
When publishing an npm package, use . Two footguns matter more than config detail:
build.lib- Types are not emitted — add or run
vite-plugin-dtsseparately.tsc --emitDeclarationOnly - Peer dependencies MUST be externalized — unlisted peers get bundled into your library, causing duplicate-runtime errors in consumers.
typescript
// 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'], // every peer dep
},
}发布npm包时,使用 。有两个陷阱比配置细节更重要:
build.lib- 不会生成类型文件——需添加 或单独运行
vite-plugin-dts。tsc --emitDeclarationOnly - 必须外部化peer依赖——未列出的peer依赖会被打包到库中,导致消费者出现重复运行时错误。
typescript
// 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'], // 所有peer依赖
},
}SSR Externals
SSR外部依赖
Bare setups are framework-author territory. Most apps should use Nuxt, Remix, SvelteKit, Astro, or TanStack Start instead. What you will tweak as a framework user is the externals config when deps break in SSR:
createServer({ middlewareMode: true })typescript
// vite.config.ts — ssr options
ssr: {
external: ['node-native-package'], // keep as require() in SSR bundle
noExternal: ['esm-only-package'], // force-bundle into SSR output (fixes most SSR errors)
target: 'node', // 'node' or 'webworker'
}纯 设置属于框架开发者的范畴。大多数应用应使用Nuxt、Remix、SvelteKit、Astro或TanStack Start。作为框架用户,你需要调整的是当依赖在SSR中出现问题时的外部依赖配置:
createServer({ middlewareMode: true })typescript
// vite.config.ts — ssr选项
ssr: {
external: ['node-native-package'], // 在SSR包中保留为require()形式
noExternal: ['esm-only-package'], // 强制打包到SSR输出中(修复大多数SSR错误)
target: 'node', // 可选值 'node' 或 'webworker'
}Dependency Pre-Bundling
依赖预打包
Vite pre-bundles dependencies to convert CJS/UMD to ESM and reduce request count.
typescript
// vite.config.ts — optimizeDeps
optimizeDeps: {
include: [
'lodash-es', // force pre-bundle known heavy deps
'cjs-package', // CJS deps that cause interop issues
'deep-lib/components/**', // glob for deep imports
],
exclude: ['local-esm-package'], // must be valid ESM if excluded
force: true, // ignore cache, re-optimize (temporary debugging)
}Vite预打包依赖以将CJS/UMD转换为ESM并减少请求数量。
typescript
// vite.config.ts — optimizeDeps
optimizeDeps: {
include: [
'lodash-es', // 强制预打包已知的重型依赖
'cjs-package', // 存在互操作性问题的CJS依赖
'deep-lib/components/**', // 深层导入的glob匹配
],
exclude: ['local-esm-package'], // 排除的依赖必须是有效的ESM
force: true, // 忽略缓存,重新优化(临时调试用)
}Common Pitfalls
常见陷阱
Dev Does Not Match Build
开发与构建结果不一致
Dev uses esbuild/Rolldown for transforms; build uses Rolldown for bundling. CJS libraries can behave differently between the two. Always verify with before deploying.
vite build && vite preview开发模式使用esbuild/Rolldown进行转换;构建模式使用Rolldown进行打包。CJS库在两者中的表现可能不同。部署前务必用 验证。
vite build && vite previewStale Chunks After Deployment
部署后出现陈旧代码块
New builds produce new chunk hashes. Users with active sessions request old filenames that no longer exist. Vite has no built-in solution. Mitigations:
- Keep old files live for a deployment window
dist/assets/ - Catch dynamic import errors in your router and force a page reload
新构建会生成新的代码块哈希值。有活跃会话的用户会请求已不存在的旧文件名。Vite没有内置解决方案,可通过以下方式缓解:
- 在部署窗口期保留旧的 文件
dist/assets/ - 在路由中捕获动态导入错误并强制页面刷新
Docker and Containers
Docker与容器
Vite binds to by default, which is unreachable from outside a container:
localhosttypescript
// vite.config.ts — Docker/container setup
server: {
host: true, // bind 0.0.0.0
hmr: { clientPort: 3000 }, // if behind a reverse proxy
}Vite默认绑定到 ,容器外部无法访问:
localhosttypescript
// vite.config.ts — Docker/容器设置
server: {
host: true, // 绑定到0.0.0.0
hmr: { clientPort: 3000 }, // 若在反向代理后
}Monorepo File Access
单仓库文件访问
Vite restricts file serving to the project root. Packages outside root are blocked:
typescript
// vite.config.ts — monorepo file access
server: {
fs: {
allow: ['..'], // allow parent directory (workspace root)
},
}Vite限制文件服务到项目根目录,根目录外的包会被阻止:
typescript
// vite.config.ts — 单仓库文件访问
server: {
fs: {
allow: ['..'], // 允许访问父目录(工作区根目录)
},
}Anti-Patterns
反模式
typescript
// BAD: Setting envPrefix to '' exposes ALL env vars (including secrets) to the client
envPrefix: ''
// BAD: Assuming require() works in application source code — Vite is ESM-first
const lib = require('some-lib') // use import instead
// BAD: Splitting every node_module into its own chunk — creates hundreds of tiny files
manualChunks(id) {
if (id.includes('node_modules')) {
return id.split('node_modules/')[1].split('/')[0] // one chunk per package
}
}
// BAD: Not externalizing peer deps in library mode — causes duplicate runtime errors
// build.lib without rolldownOptions.external
// BAD: Using deprecated esbuild minifier
build: { minify: 'esbuild' } // use 'oxc' (default) or 'terser'
// BAD: Mutating import.meta.hot.data by reassignment
import.meta.hot.data = { count: 0 } // WRONG: must mutate properties, not reassign
import.meta.hot.data.count = 0 // CORRECTProcess anti-patterns:
- is NOT a production server — it is a smoke test for the built bundle. Deploy
vite previewto a real static host (NGINX, Cloudflare Pages, Vercel static) or use a multi-stage Dockerfile.dist/ - Expecting to type-check — it only transpiles. Type errors silently ship to production. Add
vite buildor runvite-plugin-checkerin CI.tsc --noEmit - Shipping by default — it bloats bundles ~40%, breaks source-map bundle analyzers, and is unnecessary for the 95%+ of users on modern browsers. Gate it on real analytics, not assumption.
@vitejs/plugin-legacy - Hand-rolling 30+ entries that duplicate
resolve.aliaspaths — usetsconfig.jsoninstead. Observed in Excalidraw and PostHog; avoid in new projects.vite-tsconfig-paths - Leaving stale after dep changes — pre-bundle cache causes phantom errors. Clear it when switching branches or after patching deps.
node_modules/.vite
typescript
// 错误:将envPrefix设置为''会暴露所有环境变量(包括密钥)给客户端
envPrefix: ''
// 错误:假设require()可在应用源码中工作——Vite优先支持ESM
const lib = require('some-lib') // 使用import替代
// 错误:将每个node_modules包拆分为单独的代码块——会生成数百个小文件
manualChunks(id) {
if (id.includes('node_modules')) {
return id.split('node_modules/')[1].split('/')[0] // 每个包一个代码块
}
}
// 错误:在库模式下未外部化peer依赖——会导致重复运行时错误
// build.lib未配置rolldownOptions.external
// 错误:使用已弃用的esbuild压缩工具
build: { minify: 'esbuild' } // 使用'oxc'(默认)或'terser'
// 错误:通过重新赋值修改import.meta.hot.data
import.meta.hot.data = { count: 0 } // 错误:必须修改属性,不可重新赋值
import.meta.hot.data.count = 0 // 正确流程反模式:
- 不是生产服务器——它只是构建包的冒烟测试工具。将
vite preview部署到真正的静态主机(NGINX、Cloudflare Pages、Vercel静态托管)或使用多阶段Dockerfile。dist/ - 期望 进行类型检查——它仅进行转译。类型错误会被静默地带到生产环境。添加
vite build或在CI中运行vite-plugin-checker。tsc --noEmit - 默认启用 ——它会使包体积膨胀约40%,破坏Source Map包分析工具,且对于95%以上使用现代浏览器的用户来说不必要。根据实际分析数据启用,而非主观假设。
@vitejs/plugin-legacy - 手动编写30+个 条目来复制
resolve.aliaspaths——使用tsconfig.json替代。在Excalidraw和PostHog中曾出现此类情况,新项目应避免。vite-tsconfig-paths - 依赖变更后保留陈旧的 ——预打包缓存会导致莫名错误。切换分支或更新依赖后清理该目录。
node_modules/.vite
Quick Reference
快速参考
| Pattern | When to Use |
|---|---|
| Always — provides type inference |
| Access env vars in config (explicit prefix) |
| Any TypeScript app (fills the type-check gap) |
| Instead of hand-rolled |
| CJS deps causing interop issues |
| Route API requests to backend in dev |
| Docker, containers, remote access |
| Pre-transform hot-path routes |
| Publishing npm packages |
| Vendor bundle splitting |
| Debug slow dev server |
| Smoke-test prod bundle locally (NOT a prod server) |
| 模式 | 适用场景 |
|---|---|
| 始终使用——提供类型推断 |
| 在配置中访问环境变量(明确前缀) |
| 所有TypeScript应用(填补类型检查空白) |
| 替代手动编写的 |
| 存在互操作性问题的CJS依赖 |
| 开发环境中将API请求路由到后端 |
| Docker、容器、远程访问场景 |
| 预热热点路由 |
| 发布npm包 |
| 依赖包代码分割 |
| 调试缓慢的开发服务器 |
| 本地冒烟测试生产包(不是生产服务器) |
Related Skills
相关技能
- — React component patterns
frontend-patterns - — containerized dev with Vite
docker-patterns - — alternative bundler for Next.js
nextjs-turbopack
- — React组件模式
frontend-patterns - — Vite容器化开发
docker-patterns - — Next.js的替代打包工具
nextjs-turbopack