vite-patterns

vite-patterns

熱門

Vite 建置工具的使用模式与最佳实践,涵盖设定档配置、外挂程式(plugins)、HMR 热重载、环境变数、Proxy 代理设定、SSR 服务端渲染、函式库模式(library mode)、相依套件预打包(dependency pre-bundling)以及打包建置优化。当处理 `vite.config.ts`、Vite 外挂程式或基于 Vite 的专案时启用。

24萬星標
3.6萬分支
更新於 2026/7/29
SKILL.md
唯讀
名稱
vite-patterns
描述

Vite 建置工具的使用模式与最佳实践,涵盖设定档配置、外挂程式(plugins)、HMR 热重载、环境变数、Proxy 代理设定、SSR 服务端渲染、函式库模式(library mode)、相依套件预打包(dependency pre-bundling)以及打包建置优化。当处理 `vite.config.ts`、Vite 外挂程式或基于 Vite 的专案时启用。

Vite Patterns

适用于 Vite 8+ 专案的建置工具与开发伺服器(dev server)设计模式。涵盖配置设定、环境变数、Proxy 代理设定、函式库模式、相依套件预打包,以及常见的生产环境陷阱。

使用时机

  • 配置 vite.config.tsvite.config.js
  • 设定环境变数或 .env 档案
  • 为后端 API 配置开发伺服器 Proxy 代理
  • 优化建置输出成果(chunks、程式码压缩、静态资源)
  • 使用 build.lib 发布函式库套件
  • 排查相依套件预打包(dependency pre-bundling)或 CJS/ESM 互操作性问题
  • 除错 HMR、开发伺服器或打包建置错误
  • 选择或配置 Vite 外挂程式(plugins)的顺序

运作原理

  • 开发模式(Dev mode):直接以原生 ESM 提供原始码档案,无需提前打包。转换操作会在模组请求时按需执行,这正是冷启动快速且 HMR 热重载极其精确的原因。
  • 打包模式(Build mode):在生产环境中使用 Rolldown (v7+) 或 Rollup (v5–v6) 进行应用程式打包,结合 Tree-shaking 摇树优化、代码分割(code-splitting)以及基于 Oxc 的程式码压缩。
  • 相依套件预打包(Dependency pre-bundling):利用 esbuild 将 CJS/UMD 格式的相依套件一次性转换为 ESM,并将结果快取至 node_modules/.vite 下,后续启动即可直接略过处理。
  • 外挂程式(Plugins):在开发与打包模式间共享统一的介面 — 同一个外挂程式物件既可作用于开发伺服器的按需转换,也能套用于生产环境的建置管线(build pipeline)。
  • 环境变数(Environment variables):在打包建置时静态内联(inlined)。带有 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 '/' 部署静态资源的公开基础路径(Public base path)
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 在 Worker 线程中执行 tsc + ESLint 并带有 HMR 盖层 任何 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 仅进行转译(transpile),不会执行型别检查(type-check)。除非加入 vite-plugin-checker 或在 CI 流程中运行 tsc --noEmit,否则型别错误将被默默带入生产环境。

撰写自订外挂程式

亲自撰写外挂程式的情况较少见 — 大部分需求都有现成外挂可用。若确实需要,建议先在 vite.config.ts 中撰写内联(inline)外挂,只有需要跨专案复用时才独立抽离。

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

关键钩子(Hooks): transform(修改源码)、resolveId + load(虚构模组/Virtual modules)、transformIndexHtml(注入 HTML)、configureServer(新增开发 Middlewares)、hotUpdate(自订 HMR — 在 v7+ 中替代了已废弃的 handleHotUpdate)。

虚构模组(Virtual modules) 遵从 \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。只有在开发自订状态管理(state stores)、开发工具或跨框架的通用工具(且需要在更新间维持状态)时,才需要直接使用 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 程式码都会在生产环境打包时被 Tree-shaking 清除 — 无需手动进行防范判断。

环境变数

Vite 会按以下顺序载入:.env.env.local.env.[mode] 以及 .env.[mode].local(后载入的覆盖先载入的);*.local 档案已被 gitignore 忽略,用于存放本地方案的敏感资讯。

客户端存取

只有带有 VITE_ 前缀的变数才会暴露给客户端程式码:

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             // 布林值
在配置中使用环境变数
// 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_ 的变数在打包建置时都会被静态替换嵌入到客户端 bundle 中。代码压缩、Base64 编码或禁用 Source Map 都无法隐匿它。有心攻击者可以轻松从发布出来的 JavaScript 档案中提取任何 VITE_ 变数。

原则: 只有公开数值(API 网址、功能开关 Feature Flags、公开金钥)才能放入 VITE_ 变数中。敏感机密(API Tokens、数据库连接字串、私钥)必须保存在后端 API 或 Serverless 函数中。

loadEnv('') 的安全陷阱
// 错误示范:将 '' 作为第三个参数会载入所有环境变数 — 包括服务端机密 —
// 并使其可能透讨 `define` 被内联泄露到客户端程式码中。
const env = loadEnv(mode, process.cwd(), '')

// 正确示范:明确指定允许的前缀清单
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])
生产环境中的 Source Maps

生产环境的 Source Map 会暴露应用程序的完整原始码。除非将 Source Map 上传至错误追踪系统(如 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'
}

效能

避免使用 Barrel Files

Barrel files(例如在一个 index.ts 中集中重新导出整个目录的内容)会导致 Vite 即使只引用一个符号,也必须载入被重新导出的所有档案。这是官方文件中标注的第一大开发伺服器效能杀手。

// 错误 — 汇入单个工具函式却强迫 Vite 载入整个 barrel
import { slash } from '@/utils'

// 正确 — 直接汇入,仅载入所需档案
import { slash } from '@/utils/slash'
明确指定汇入档案副档名

每次省略副档名都会导致系统透过 resolve.extensions 进行多达 6 次的档案系统检查。在大型程式码库中,这种开销积累起来非常可观。

// 错误
import Component from './Component'

// 正确
import Component from './Component.tsx'

收窄 tsconfig.jsonallowImportingTsExtensionsresolve.extensions,仅保留实际用到的副档名。

热点路由预热

server.warmup.clientFiles 可以在浏览器发起请求之前,预先转换已知的高频入口 — 从而消除大型应用冷启动时的请求瀑布流(request waterfall)。

// vite.config.ts
server: {
  warmup: {
    clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
  },
}
开发伺服器变慢排查

vite dev 感觉卡顿时,可以使用 vite --profile 启动,在应用中操作几下后按下 p+enter 储存 .cpuprofile。将文件载入至 Speedscope 中,以排查是哪个外挂耗时过长 — 通常是社群外挂中的 buildStartconfigconfigResolved 钩子。

函式库模式

当需要发布 npm 套件时,使用 build.lib。相比配置细节,有两个陷阱需要格外注意:

  1. 型别不会自动产生 — 请搭配 vite-plugin-dts 外挂或单独执行 tsc --emitDeclarationOnly
  2. Peer dependencies 必须外置化(externalized) — 未被排除的 Peer 套件会被打包进你的函式库中,导致使用者的专案出现重复运行时错误(duplicate-runtime errors)。
// 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

简单的 createServer({ middlewareMode: true }) 属于框架开发者关注的范畴。大多数应用建议直接选择 Nuxt, Remix, SvelteKit, Astro 或 TanStack Start。作为框架使用者,当相依套件在 SSR 模式报错时,你唯一需要微调的是外部化配置(externals config):

//

<!-- truncated for translation batch; full body continues in source -->