跳转至

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)并在本地删除,否则请禁用:

build: {
  sourcemap: false,                                  // デフォルト — このままにする
}

.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 次文件系统检查。在大型代码库中会累积。

// BAD
import Component from './Component'

// GOOD
import Component from './Component.tsx'

请将 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。有两个比配置细节更重要的陷阱:

  1. 类型不会被输出 — 请添加 vite-plugin-dts,或单独运行 tsc --emitDeclarationOnly。
  2. 必须外部化对等依赖 — 如果未列出的对等依赖被打包进库中,会导致使用者出现重复运行时错误。
// 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 限制向项目根目录提供文件。根目录外的包会被阻止:

// vite.config.ts — モノレポのファイルアクセス
server: {
  fs: {
    allow: ['..'],                             // 親ディレクトリ(ワークスペースルート)を許可
  },
}

反模式

// 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