Environment API 是 Vite 7 引入的核心架构升级。它将之前单一的 dev/build 上下文拆分为多个独立的环境 (client、SSR、自定义),每个环境拥有独立的模块图、插件容器和 HMR 通道
scss
Vite 6 及之前 Vite 7
┌─────────────────┐ ┌──────────────────────┐
│ ViteDevServer │ │ ViteDevServer │
│ ├─ moduleGraph │ │ ├─ moduleGraph (兼容)│
│ ├─ pluginCont. │ │ │ │
│ └─ watcher │ │ └─ environments │
└─────────────────┘ │ ├─ client │
│ │ ├─ moduleGraph
│ │ ├─ pluginContainer
│ │ └─ hot (HMR)
│ │
│ ├─ ssr
│ │ ├─ moduleGraph
│ │ ├─ pluginContainer
│ │ └─ hot (HMR)
│ │
│ └─ custom
│ └─ ...
└──────────────────────┘
完整的环境初始化流程
scss
resolveConfig(inlineConfig, command)
│
│ 配置层面的环境准备
├─ environments: { client: {}, ssr: {} } ← 默认创建
├─ dev.warmup,ssr.optimizeDeps,ssr.resolve,ssr.build.ssrEmitAssets → environments ← 向后兼容
├─ getDefaultEnvironmentOptions(config) ← 默认选项
├─ mergeConfig(默认, 用户配置) ← 合并
├─ runConfigEnvironmentHook(environments, hooks) ← 插件修改
├─ resolveEnvironmentOptions(env) → ResolvedEnvironmentOptions ← 解析环境配置
└─ client.optimizeDeps,client.resolve, ssr → config ← 回写兼容层
│
▼
_createServer(inlineConfig, { listen })
│
│ 运行时的环境实例化
├─ for each environment (并行):
│ │
│ ├─ DevEnvironment.constructor(name, config, { ws })
│ │ ├─ new EnvironmentModuleGraph(name, resolveId)
│ │ ├─ normalizeHotChannel(ws)
│ │ │ └─ 注册 invoke 处理器 (fetchModule, getBuiltins)
│ │ ├─ depsOptimizer = createDepsOptimizer(this)
│ │ └─ 注册 vite:invalidate 事件
│ │
│ └─ environment.init({ watcher, previousInstance })
│ └─ createEnvironmentPluginContainer(this, plugins, watcher)
│ └─ resolveEnvironmentPlugins(environment)
│ 过滤每个环境的插件列表
│
├─ createPluginContainer(environments) ← 兼容层
│
└─ server.listen()
└─ for each environment:
├─ hot.listen() ← 启动 HMR
├─ depsOptimizer.init() ← 预构建依赖
└─ warmupFiles(server, environment) ← 预热文件
环境的生命周期
scss
创建 (constructor)
│
▼
初始化 (init)
└─ 插件容器创建
└─ resolveEnvironmentPlugins → 环境级插件列表
│
▼
监听 (listen)
├─ HMR 启动
├─ 依赖预构建
└─ 文件预热
│
▼
运行中
├─ transformRequest(url) ← 按需转换
├─ reloadModule(module) ← HMR 触发
├─ fetchModule(id) ← ModuleRunner 获取
└─ waitForRequestsIdle() ← 等待爬取完成
│
▼
关闭 (close)
├─ 插件容器关闭
├─ 依赖优化器关闭
├─ HMR 通道关闭
└─ 等待 pending 请求完成
环境类继承层次
scss
PartialEnvironment (baseEnvironment.ts)
│ name, config(Proxy), logger, getTopLevelConfig()
│
└─ BaseEnvironment (baseEnvironment.ts)
│ plugins getter, _initiated
│
├─ DevEnvironment (server/environment.ts)
│ mode = 'dev'
│ moduleGraph, pluginContainer, depsOptimizer, hot
│ transformRequest(), reloadModule(), fetchModule()
│ waitForRequestsIdle()
│
│ ├─ RunnableDevEnvironment (server/environments/runnableEnvironment.ts)
│ │ runner getter (ModuleRunner)
│ │
│ └─ FetchableDevEnvironment (server/environments/fetchableEnvironments.ts)
│ fetchModule() 远程访问
│
├─ BuildEnvironment (build.ts)
│ mode = 'build'
│ isBuilt, buildOptions
│ pluginContainer, rollupWatcher
│
├─ ScanEnvironment (optimizer/scan.ts)
│ mode = 'scan'
│ pluginContainer (简化)
│
└─ UnknownEnvironment (baseEnvironment.ts)
mode = 'unknown'
占位类型,用于未知环境
三种内置环境对比
| 维度 | DevEnvironment |
BuildEnvironment |
ScanEnvironment |
|---|---|---|---|
| mode | 'dev' |
'build' |
'scan' |
| 场景 | 开发服务器 | 生产构建 | 依赖预构建扫描 |
| 模块图 | EnvironmentModuleGraph |
无(直接 Rollup) |
简化图 |
| 插件容器 | EnvironmentPluginContainer |
通过 Rollup 插件 |
简化容器 |
| HMR | 完整支持 | 无 | 无 |
| 依赖优化 | createDepsOptimizer |
缓存读取 | 扫描触发 |
| 输出 | 转换后的 ESM |
bundle 文件 |
依赖列表 |
| 生命周期 | _createServer 中创建 |
createBuilder 中创建 |
scanImports 中创建 |
辅助函数
环境状态隔离:perEnvironmentState
Vite 存在多 Environment:client(浏览器环境)、ssr(服务端渲染环境);插件经常需要区分环境保存独立临时状态,不能共用一份变量,否则 client/ssr 状态互相污染。该方法自动根据当前 environment 实例,惰性创建、缓存隔离状态 。选用 WeakMap 是因为 Environment 实例销毁时,无需手动清理,GC 自动回收对应的 State,防止内存泄漏
javascript
export function perEnvironmentState(
initial,
) {
const stateMap = new WeakMap()
return function (context) {
const { environment } = context
let state = stateMap.get(environment)
if (!state) {
state = initial(environment)
stateMap.set(environment, state)
}
return state
}
}
生成环境的默认基础配置:getDefaultEnvironmentOptions
生成 Environment 初始化的默认基础配置模板函数
- 遵循 "大部分复用、少量差异化覆盖" 原则 :
define、dev、build、resolve.alias、resolve.extensions等全部继承,避免重复复制大量配置代码 - 隔离「全局根配置」与「环境配置」的冲突字段 :主动清空
resolve.mainFields/resolve.conditions,Vite内部的Client环境、SSR环境拥有各自独立默认的mainFields、conditions,不能直接复用顶层配置
javascript
export function getDefaultEnvironmentOptions(
config,
) {
return {
define: config.define,
resolve: {
...config.resolve,
mainFields: undefined,
conditions: undefined,
},
dev: config.dev,
build: config.build,
}
}
执行插件的环境配置钩子:runConfigEnvironmentHook
执行所有插件的 configEnvironment 钩子,对各个 Environment 的环境配置进行插件层面修改 。插件可以读取 / 返回环境配置片段,用来差异化修改 client/ssr 各自独立的环境配置
- 创建轻量化插件上下文
BasicMinimalPluginContext(此时Environment还未创建,不存在环境实例):上下文提供插件基础能力包括日志输出、路径工具等,满足configEnvironment钩子最小运行需求 - 调用
getSortedPluginsByHook:筛选拥有configEnvironment钩子的插件,循环遍历插件,按插件执行优先级排序- 取出插件定义的
configEnvironment,调用getHookHandler统一提取真正执行函数 - 遍历每一个环境,执行插件钩子
- 使用
mergeConfig将执行结果深度合并进当前环境配置 ,覆盖更新environments[name]
- 取出插件定义的
javascript
async function runConfigEnvironmentHook(
environments,
plugins,
logger,
configEnv,
isSsrTargetWebworkerSet,
) {
const context = new BasicMinimalPluginContext(basePluginContextMeta, logger)
const environmentNames = Object.keys(environments)
for (const p of getSortedPluginsByHook('configEnvironment', plugins)) {
const hook = p.configEnvironment
const handler = getHookHandler(hook)
for (const name of environmentNames) {
const res = await handler.call(context, name, environments[name], {
...configEnv,
isSsrTargetWebworker: isSsrTargetWebworkerSet && name === 'ssr',
})
if (res) {
environments[name] = mergeConfig(environments[name], res)
}
}
}
}
标准化环境配置:resolveEnvironmentOptions
Vite 支持多环境(client / ssr / 自定义环境),每个环境可以传入局部配置,该函数是环境配置的归一解析入口 ,输入原始的环境配置片段,填充缺失默认值,安全风险校验(define process.env 警告),递归解析子模块 resolve/optimizeDeps/dev/build,根据 consumer(client/server)、环境类型自动推导隐式规则,输出一份结构完整、规则归一、校验通过的 EnvironmentOptions,供构造 Environment 实例使用
- 判断是否为浏览器客户端环境,推导
consumer(消费端标识)- 用户显式传
options.consumer优先使用 - 未传入时自动推导:
client环境 →consumer: 'client',其余(ssr)→server
- 用户显式传
- 标记当前是否是
SSR WebWorker特殊环境 ,该环境会修改keepProcessEnv、模块解析行为,作为后续分支判断条件 - 安全校验逻辑(重点)
- 调用子函数
resolveEnvironmentResolveOptions,解析模块解析配置alias/extensions/mainFields/export conditions/preserveSymlinks等,根据consumer(client/server)区分包导出条件,生成环境专属resolve配置 - 返回标准化的环境配置
- 控制是否保留源码中
process.env不被静态替换:SSR(非WebWorker) 默认保留process.env,运行时读取Node环境变量,Browser默认全部通过define静态替换,不保留运行时访问 - 调用
resolveDepOptimizationOptions解析依赖预构建配置optimizeDeps:根据client/server填充默认值、整合include/exclude、构建缓存相关配置 - 调用
resolveDevEnvironmentOptions解析开发环境专属配置(模块预转换、开发服务器相关环境选项) - 调用
resolveBuildEnvironmentOptions解析构建阶段配置
- 控制是否保留源码中
javascript
function resolveEnvironmentOptions(
options,
alia,
preserveSymlinks,
forceOptimizeDeps,
logger,
environmentName,
isSsrTargetWebworkerSet,
preTransformRequests,
) {
const isClientEnvironment = environmentName === 'client'
const consumer =
options.consumer ?? (isClientEnvironment ? 'client' : 'server')
const isSsrTargetWebworkerEnvironment =
isSsrTargetWebworkerSet && environmentName === 'ssr'
if (options.define?.['process.env']) {
const processEnvDefine = options.define['process.env']
if (typeof processEnvDefine === 'object') {
const pathKey = Object.entries(processEnvDefine).find(
([key, value]) => key.toLowerCase() === 'path' && !!value,
)?.[0]
if (pathKey) {
logger.warnOnce(
colors.yellow(
`The \`define\` option contains an object with ${JSON.stringify(pathKey)} for "process.env" key. ` +
'It looks like you may have passed the entire `process.env` object to `define`, ' +
'which can unintentionally expose all environment variables. ' +
'This poses a security risk and is discouraged.',
),
)
}
}
}
const resolve = resolveEnvironmentResolveOptions(
options.resolve,
alias,
preserveSymlinks,
logger,
consumer,
isSsrTargetWebworkerEnvironment,
)
return {
define: options.define,
resolve,
// 控制是否保留源码中 process.env 不被静态替换
keepProcessEnv:
options.keepProcessEnv ??
(isSsrTargetWebworkerEnvironment ? false : consumer === 'server'),
consumer,
optimizeDeps: resolveDepOptimizationOptions(
options.optimizeDeps,
resolve.preserveSymlinks,
forceOptimizeDeps,
consumer,
),
dev: resolveDevEnvironmentOptions(
options.dev,
environmentName,
consumer,
preTransformRequests,
),
build: resolveBuildEnvironmentOptions(
options.build ?? {},
logger,
consumer,
),
plugins: undefined,
}
}
标准化模块解析配置:resolveEnvironmentResolveOptions
规范化、填充默认值、生成 Environment 专属的模块解析配置对象 resolve 。模块解析配置控制 import 路径查找逻辑:alias、后缀、mainFields、导出条件 conditions、Node 内置模块处理、软链接等
- 分层合并配置:系统默认
configDefaults.resolve→ 环境自动推导默认 → 用户自定义覆盖:先填充环境专属智能默认,再接纳用户配置覆盖,兼顾开箱即用和可定制 - 统一生成环境差异化模块解析规则 :集中管理
client / SSR / SSR WebWorker三套包解析策略;一处收敛所有分支逻辑,Environment不需要自行判断环境去设置mainFields/conditions/builtins,SSR WebWorker运行环境介于浏览器和Node之间,复用客户端包解析规则,但同时存在noExternal时禁用内置模块识别mainFields默认值consumer未定义或consumer = client或SSR WebWorker环境,使用DEFAULT_CLIENT_MAIN_FIELDS(优先读取browser字段)- 标准
SSR服务端,使用DEFAULT_SERVER_MAIN_FIELDS,忽略package.json browser字段
conditions:package.json exports的导出条件筛选规则Client / SSR WebWorker:完整客户端条件(包含browser)- 标准
SSR服务端:过滤删除browser条件,不读取浏览器导出分支,优先Node实现
builtins:识别哪些模块视为Node内置模块- 如果是
SSR WebWorker并且开启noExternal: true→ 空数组(不识别内置模块) - 其余标准
SSR→ 填入nodeLikeBuiltins(识别fs/path等内置包) client浏览器环境 → 空数组,不存在Node内置模块
- 如果是
- 锁定全局共享参数(
alias/preserveSymlinks):明确约束,别名、软链接策略全局统一,禁止单个环境独立篡改,保证项目路径解析全局行为一致
javascript
function resolveEnvironmentResolveOptions(
resolve,
alias,
preserveSymlinks,
logger,
consumer,
isSsrTargetWebworkerEnvironment,
) {
const resolvedResolve = mergeWithDefaults(
{
...configDefaults.resolve,
mainFields:
consumer === undefined ||
consumer === 'client' ||
isSsrTargetWebworkerEnvironment
? DEFAULT_CLIENT_MAIN_FIELDS
: DEFAULT_SERVER_MAIN_FIELDS,
conditions:
consumer === undefined ||
consumer === 'client' ||
isSsrTargetWebworkerEnvironment
? DEFAULT_CLIENT_CONDITIONS
: DEFAULT_SERVER_CONDITIONS.filter((c) => c !== 'browser'),
builtins:
resolve?.builtins ??
(consumer === 'server'
? isSsrTargetWebworkerEnvironment && resolve?.noExternal === true
? []
: nodeLikeBuiltins
: []),
},
resolve ?? {},
)
resolvedResolve.preserveSymlinks = preserveSymlinks
resolvedResolve.alias = alias
if (
resolve?.browserField === false &&
resolvedResolve.mainFields.includes('browser')
) {
logger.warn(
colors.yellow(
`\`resolve.browserField\` is set to false, but the option is removed in favour of ` +
`the 'browser' string in \`resolve.mainFields\`. You may want to update \`resolve.mainFields\` ` +
`to remove the 'browser' string and preserve the previous browser behaviour.`,
),
)
}
return resolvedResolve
}
const DEFAULT_MAIN_FIELDS = [
'browser',
'module',
'jsnext:main',
'jsnext',
]
export const DEFAULT_CLIENT_MAIN_FIELDS =
Object.freeze(DEFAULT_MAIN_FIELDS)
export const DEFAULT_SERVER_MAIN_FIELDS = Object.freeze(
DEFAULT_MAIN_FIELDS.filter((f) => f !== 'browser'),
)
export const DEV_PROD_CONDITION = `development|production`
const DEFAULT_CONDITIONS = ['module', 'browser', 'node', DEV_PROD_CONDITION]
export const DEFAULT_CLIENT_CONDITIONS = Object.freeze(
DEFAULT_CONDITIONS.filter((c) => c !== 'node'),
)
export const DEFAULT_SERVER_CONDITIONS = Object.freeze(
DEFAULT_CONDITIONS.filter((c) => c !== 'browser'),
)
标准化依赖预构建配置:resolveDepOptimizationOptions
生成补齐环境差异化默认、合并用户自定义配置的标准化 optimizeDeps 对象,供当前 Environment 的依赖预构建流程使用
- 标准化合并范式 :采用框架基础默认值
configDefaults.optimizeDeps→ 环境差异化覆写 → 用户配置覆盖三层合并模型 - 为多
Environment生成环境差异化的依赖预构建配置Client:启用依赖自动发。Client需要扫描源码收集第三方依赖交给 esbuild 预构建SSR:关闭依赖自动发现(noDiscovery: true)。SSR链路模块加载模式不同,不需要自动扫描依赖,通常依靠noExternal管控外部包
- 打通全局软链接配置,保证文件解析规则统一 :把顶层
preserveSymlinks注入预构建的esbuild参数;保证模块路径解析 和esbuild预构建两套逻辑对于软链接处理行为一致,规避软链接场景下缓存、解析不一致问题 - 统一
force强制预构建策略优先级:顶层强制标记优先于默认配置,支持开发服务器全局触发重新预构建,忽略缓存
javascript
function resolveDepOptimizationOptions(
optimizeDeps,
preserveSymlinks,
forceOptimizeDeps,
consumer,
) {
return mergeWithDefaults(
{
...configDefaults.optimizeDeps,
disabled: undefined,
noDiscovery: consumer !== 'client',
esbuildOptions: {
preserveSymlinks,
},
force: forceOptimizeDeps ?? configDefaults.optimizeDeps.force,
},
optimizeDeps ?? {},
)
}
标准化开发环境配置:resolveDevEnvironmentOptions
- 统一三层合并范式 :全局默认
configDefaults.dev→ 环境智能推导默认值 → 用户自定义配置覆盖 - 规范化开发环境 (
dev) 专属配置,自动区分client/ssr差异化默认行为 :集中一套规则,根据consumer、environmentName自动填充开箱可用的默认值,减轻用户配置负担sourcemapIgnoreList:默认使用内置函数判断路径是否属于node_modules。sourcemap忽略node_modules内文件,不生成 / 不展示第三方包源码映射,缩减资源体积、提升调试体验preTransformRequests:预转换请求开关优先级上层传入值 > 自动推导(consumer === client开启预转换)。浏览器环境支持模块预转换,SSR默认关闭预转换createEnvironment:根据环境名称绑定对应的环境工厂函数,控制当前环境如何实例化开发阶段Environmentclient→defaultCreateClientDevEnvironment(创建浏览器开发环境)ssr→defaultCreateDevEnvironment(通用服务端开发环境)
recoverable:是否开启可恢复错误机制 ,仅客户端开启。当模块转换报错时,页面展示ErrorOverlay浮窗,服务不崩溃,SSR关闭,编译异常直接抛出moduleRunnerTransform:是否启用ModuleRunner转换逻辑。仅SSR服务端环境开启,用于服务端模块运行时转换
- 抹平
sourcemapIgnoreList布尔值与函数的类型差异 :用户可写false关闭过滤,内部统一标准化为函数类型,下游插件 / 转换逻辑无需重复类型判断
javascript
export function resolveDevEnvironmentOptions(
dev,
environmentName,
consumer,
preTransformRequest,
) {
const resolved = mergeWithDefaults(
{
...configDefaults.dev,
sourcemapIgnoreList: isInNodeModules,
preTransformRequests: preTransformRequest ?? consumer === 'client',
createEnvironment:
environmentName === 'client'
? defaultCreateClientDevEnvironment
: defaultCreateDevEnvironment,
recoverable: consumer === 'client',
moduleRunnerTransform: consumer === 'server',
},
dev ?? {},
)
return {
...resolved,
sourcemapIgnoreList:
resolved.sourcemapIgnoreList === false
? () => false
: resolved.sourcemapIgnoreList,
}
}
标准化构建环境配置:resolveBuildEnvironmentOptions
规范化、补齐默认值、处理兼容迁移,生成构建阶段 Environment 的 build 配置 ,只处理 build 相关选项,区分 consumer: client(浏览器产物)/ server(SSR产物);同时承担废弃配置迁移兼容 (polyfillModulePreload)、参数格式标准化、多字段联动推导
- 统一三层合并范式 :全局默认
_buildEnvironmentOptionsDefaults→ 环境智能推导默认值 → 用户自定义配置覆盖 - 废弃 API 平滑迁移层 :集中处理
polyfillModulePreload旧配置自动转换 + 警告;一处完成兼容,不需要插件、构建链路到处做兼容判断 - 多类参数格式标准化
cssCodeSplit:非库模式开启CSS代码分割,lib: true(打包类库)默认关闭代码分割minify:SSR构建 → 默认不压缩false,client浏览器产物 → 默认esbuild压缩,标准化,统一格式,布尔 / 字符串归一ssr:自动标记是否为SSR构建目标emitAssets:Client构建输出静态资源(图片、字体),SSR构建默认不emit资源createEnvironment:绑定构建环境工厂,统一创建BuildEnvironmentmodulePreload:多层格式归一化,支持true/false/ 对象三种写法统一转换target:别名常量转换cssTarget:自动兜底复用target,用户不设置CSS编译目标,则复用顶层target,JS与CSS构建目标保持一致cssMinify:SSR服务端默认开启css压缩esbuild,Client跟随minify开关,开启JS压缩则同步开启CSS压缩
javascript
export function resolveBuildEnvironmentOptions(
raw,
logger,
consumer,
) {
const deprecatedPolyfillModulePreload = raw.polyfillModulePreload
const { polyfillModulePreload, ...rest } = raw
raw = rest
if (deprecatedPolyfillModulePreload !== undefined) {
logger.warn(
'polyfillModulePreload is deprecated. Use modulePreload.polyfill instead.',
)
}
if (
deprecatedPolyfillModulePreload === false &&
raw.modulePreload === undefined
) {
raw.modulePreload = { polyfill: false }
}
const merged = mergeWithDefaults(
{
..._buildEnvironmentOptionsDefaults,
cssCodeSplit: !raw.lib,
minify: consumer === 'server' ? false : 'esbuild',
ssr: consumer === 'server',
emitAssets: consumer === 'client',
createEnvironment: (name, config) => new BuildEnvironment(name, config),
},
raw,
)
if (merged.target === 'baseline-widely-available') {
merged.target = ESBUILD_BASELINE_WIDELY_AVAILABLE_TARGET
}
if ((merged.minify as string) === 'false') {
merged.minify = false
} else if (merged.minify === true) {
merged.minify = 'esbuild'
}
const defaultModulePreload = {
polyfill: true,
}
const resolved = {
...merged,
cssTarget: merged.cssTarget ?? merged.target,
cssMinify:
merged.cssMinify ?? (consumer === 'server' ? 'esbuild' : !!merged.minify),
modulePreload:
merged.modulePreload === false
? false
: merged.modulePreload === true
? defaultModulePreload
: {
...defaultModulePreload,
...merged.modulePreload,
},
}
return resolved
}
export const ESBUILD_BASELINE_WIDELY_AVAILABLE_TARGET = [
'chrome107',
'edge107',
'firefox104',
'safari16',
]
默认配置:configDefaults
Vite 顶层根配置全局默认常量对象 ,存储 Vite UserConfig → ResolvedConfig 解析链路使用的全部内置默认配置基准 ,当用户 vite.config 缺少对应配置项时,以此对象字段作为兜底,同时作为 getDefaultEnvironmentOptions 等环境配置派生函数的数据源
- 基础通用配置
define:全局变量注入,默认不注入任何process.env、全局常量base:应用部署基础路径,默认/publicDir:静态资源目录,默认publicappType:默认按单页面应用处理路由、HTML逻辑,可选mpa/customenvDir:环境变量文件存放目录,默认读取root根目录.env文件envPrefix:环境变量白名单前缀,默认只暴露前缀VITE_*变量给客户端logLevel/customLogger/clearScreen:日志与控制台,默认info级别,启动服务自动清屏
dev开发基础默认配置warmup:预预热模块列表,默认空sourcemap:默认开启JS sourcemapsourcemapIgnoreList:sourcemap忽略文件过滤函数,交由环境解析阶段重新赋值isInNodeModules
build构建默认配置:引用构建选项默认常量buildEnvironmentOptionsDefaults,供顶层build和构建Environment共享基础模板- resolve 模块解析默认(核心路径 / 依赖解析规则)
extensions:导入路径自动补文件后缀,默认支持前端常用脚本后缀['.mjs','.js','.ts','.jsx','.tsx','.json']externalConditions:包 exports 默认导出条件alias:路径别名映射,默认无路径别名preserveSymlinks:默认跟随软链接真实路径,使用真实文件所在目录dedupe:强制依赖去重,多个包引入同一个依赖时,强制使用项目根下单一版本,默认不主动去重noExternal:SSR 专用,强制把外部依赖纳入打包,不交给 Node require,默认空external:开发 / 构建阶段排除依赖,不进行打包、不预构建,默认空
- 插件、HTML、资源相关
plugins:插件,默认空数组html:html 解析、处理配置,默认不给index.html内 vite 注入的<script>标签附加 nonce(用于 CSP 内容安全策略)css:CSS 解析、预处理器、压缩、模块默认配置json:json 解析、处理配置,默认支持 JSON 文件具名导入和简单 JSON 转为静态字符串,复杂结构保留对象形式assetsInclude:拓展识别为静态资源的文件后缀,默认使用使用 Vite 内置静态资源后缀列表(图片、字体等)
- 服务相关配置
server:开发服务器(中间件、端口、cors、fs 白名单等默认值)preview:vite preview预览 dist 产物服务器配置,默认端口 DEFAULT_PREVIEW_PORT(4173)builder:实验性多包构建器配置(并行构建),独立常量
- worker WebWorker 默认配置:WebWorker 默认打包格式 iife,worker 默认独立空插件列表
- optimizeDeps 依赖预构建默认
disabled:构建阶段默认关闭依赖预构建(预构建仅用于开发环境)force:默认不强制重建缓存include:强制纳入预构建依赖,默认空exclude:排除预构建,默认空holdUntilCrawlEnd:等待模块扫描完成再启动服务,默认开启needsInterop:强制对指定包开启 CJS 互操作包装,默认空extensions:预构建扫描node_modules 内依赖时,识别哪些后缀视为可执行模块,用于解析第三方包内部引入文件,默认空
- 顶层 SSR 相关默认配置
ssr:ssrConfigDefaults,控制 SSR 外部化、加载行为 - 多环境架构
environments:用户未自定义环境时为空对象(仅内置 client /ssr) - experimental /future/legacy 兼容性分组
experimental:实验性功能开关importGlobRestoreExtension:glob 导入恢复文件后缀,默认关闭renderBuiltUrl:自定义构建后资源 URL 处理钩子,默认未启用hmrPartialAccept:模块局部热更新,默认关闭
future:渐进式破坏性变更开关 ,用于提前启用下一个大版本行为,平滑升级,默认undefined,保持当前版本兼容行为;主动设为 true 启用新版逻辑legacy:遗留兼容开关,用于兼容旧浏览器、旧行为skipWebSocketTokenCheck:跳过 HMR websocket 安全 token 校验,默认关闭(安全校验开启)
javascript
const configDefaults = Object.freeze({
define: {},
dev: {
warmup: [],
sourcemap: { js: true },
sourcemapIgnoreList: undefined,
},
build: buildEnvironmentOptionsDefaults,
resolve: {
externalConditions: [...DEFAULT_EXTERNAL_CONDITIONS],
extensions: DEFAULT_EXTENSIONS,
dedupe: [],
noExternal: [],
external: [],
preserveSymlinks: false,
alias: [],
},
base: '/',
publicDir: 'public',
plugins: [],
html: {
cspNonce: undefined,
},
css: cssConfigDefaults,
json: {
namedExports: true,
stringify: 'auto',
},
assetsInclude: undefined,
builder: builderOptionsDefaults,
server: serverConfigDefaults,
preview: {
port: DEFAULT_PREVIEW_PORT,
},
experimental: {
importGlobRestoreExtension: false,
renderBuiltUrl: undefined,
hmrPartialAccept: false,
},
future: {
removePluginHookHandleHotUpdate: undefined,
removePluginHookSsrArgument: undefined,
removeServerModuleGraph: undefined,
removeServerHot: undefined,
removeServerTransformRequest: undefined,
removeServerWarmupRequest: undefined,
removeSsrLoadModule: undefined,
},
legacy: {
skipWebSocketTokenCheck: false,
},
logLevel: 'info',
customLogger: undefined,
clearScreen: true,
envDir: undefined,
envPrefix: 'VITE_',
worker: {
format: 'iife',
plugins: (): never[] => [],
},
optimizeDeps: {
include: [],
exclude: [],
needsInterop: [],
extensions: [],
disabled: 'build',
holdUntilCrawlEnd: true,
force: false,
},
ssr: ssrConfigDefaults,
environments: {},
appType: 'spa',
})
export const DEFAULT_EXTERNAL_CONDITIONS = Object.freeze([
'node',
'module-sync',
])
export const DEFAULT_EXTENSIONS = [
'.mjs',
'.js',
'.mts',
'.ts',
'.jsx',
'.tsx',
'.json',
]
构建环境默认配置:buildEnvironmentOptionsDefaults
构建环境(BuildEnvironment)专属默认配置常量 。单个 Environment 构建阶段的基准默认值 ,会在 resolveBuildEnvironmentOptions 作为基底,再叠加 consumer(client/server) 差异化逻辑、用户环境 build 配置覆盖
target:产物浏览器兼容目标,传给esbuild用于语法降级、内置API polyfill判定。默认值为标准化浏览器版本polyfillModulePreload:是否自动注入modulepreload polyfill,给不支持<link rel="modulepreload">的老旧浏览器兼容。旧版顶层开关,新版本迁移至modulePreload.polyfill。默认开启modulePreload:控制模块预加载<link rel="modulepreload">行为。默认开启,构建时自动生成modulepreload标签,加速模块并行加载outDir:构建产物输出目录。默认distassetsDir:静态资源(图片、字体、媒体)在outDir下的子目录名称。默认assets,产物路径dist/assets/xxx.[hash].pngassetsInlineLimit:资源内联阈值(单位byte),小于阈值的图片 / 字体转为base64内联进JS/CSS,避免额外网络请求;超出则输出独立文件。默认常量通常为4096(4kb)sourcemap:构建是否生成sourcemap。默认关闭,生产构建不输出sourcemap,减小包体积;排查线上问题可手动开启true | 'inline' | 'hidden'terserOptions:当压缩工具选择terser时,透传给terser的压缩配置。默认空对象,使用terser内置默认压缩规则。Vite默认使用esbuild压缩,只有手动指定minify: 'terser'该配置才生效rollupOptions:底层Rollup打包自定义配置。默认空对象,使用Vite自动生成的Rollup配置commonjsOptions:@rollup/plugin-commonjs插件配置,负责将CommonJS依赖转为ESMinclude:包括的文件。默认只转换node_modules内的CJS文件xtensions:识别哪些后缀当作CommonJS模块处理。默认js和cjs
dynamicImportVarsOptions:控制动态导入变量import(./${name}.js)的解析行为warnOnError:动态导入分析失败是否输出警告。默认开启exclude:不包括的文件。默认不扫描node_modules内的动态导入,提升构建速度、避免误解析第三方包
write:Rollup是否将产物写入磁盘。默认写入outDir。库模式 / 程序化构建场景可设为false,让rollup返回bundle对象,内存处理不落地文件emptyOutDir:构建前是否清空outDir。默认自动判断,若outDir在项目root内部则自动清空;若等于项目根目录不清空(防止误删源码)copyPublicDir:构建时是否把publicDir目录文件复制到输出目录。默认开启。SSR构建时常关闭,不需要拷贝前端静态资源license:是否提取第三方依赖license信息生成LICENSE文件。默认不生成。开启会收集依赖版权声明manifest:是否生成manifest.json资源清单,清单内容是原始文件名 → 带hash产物文件名映射。默认关闭。SSR、后端渲染框架、CMS项目一般需要开启,方便后端渲染时查找资源路径lib:是否开启库打包模式 。默认关闭,使用应用模式(SPA/SSR应用,生成浏览器可直接加载的产物)。库模式输出ESM / UMD / CJS供第三方导入,会默认关闭cssCodeSplit,不自动注入HTMLssrManifest:是否生成SSR资源清单ssr-manifest.json,清单用于服务端渲染,记录模块依赖关系、动态导入chunk映射。默认关闭;开启SSR渲染时需要启用ssrEmitAssets:SSR构建时是否一并输出静态资源(图片、字体等)。默认SSR构建只输出JS,不输出资源。如果需要服务端同时托管静态资源,设置truereportCompressedSize:构建结束控制台是否输出gzip /brotli压缩后的体积统计。默认开启。CI流水线想要精简日志可关闭chunkSizeWarningLimit:chunk体积告警阈值(单位kb)。默认500,当单个产物chunk超过500kb,控制台输出警告,提示进行代码分割优化watch:构建监听模式(watch增量构建)配置。默认不开启watch
javascript
const _buildEnvironmentOptionsDefaults = Object.freeze({
target: 'baseline-widely-available',
polyfillModulePreload: true,
modulePreload: true,
outDir: 'dist',
assetsDir: 'assets',
assetsInlineLimit: DEFAULT_ASSETS_INLINE_LIMIT,
sourcemap: false,
terserOptions: {},
rollupOptions: {},
commonjsOptions: {
include: [/node_modules/],
extensions: ['.js', '.cjs'],
},
dynamicImportVarsOptions: {
warnOnError: true,
exclude: [/node_modules/],
},
write: true,
emptyOutDir: null,
copyPublicDir: true,
license: false,
manifest: false,
lib: false,
ssrManifest: false,
ssrEmitAssets: false,
reportCompressedSize: true,
chunkSizeWarningLimit: 500,
watch: null,
})
export const buildEnvironmentOptionsDefaults = _buildEnvironmentOptionsDefaults
CSS 默认配置:cssConfigDefaults
Vite css 全局子配置默认模板 ,定义开发环境 + 构建环境全部 CSS / 预处理器(Sass/Less/Stylus)处理流程
transformer:指定CSS转换处理器 ,负责自动前缀补全(autoprefixer)、嵌套语法、minify、现代CSS特性降级、postcss.config.js插件执行。默认postcsspostcss:兼容生态所有postcss插件(autoprefixer/postcss-px-to-viewport/tailwindcss等),基于JS,灵活性最高lightningcss:基于Rust的高性能CSS工具;替代postcss做转译、压缩,速度更快,但无法直接兼容绝大多数postcss插件 ,适合无复杂postcss插件场景
preprocessorMaxWorkers:控制Sass / Less / Stylus等CSS预处理器 的多线程Worker数量,用于并行编译样式文件,提升开发构建速度。默认自动根据CPU核心数创建最大可用worker(Vite内部自动计算)devSourcemap:控制开发环境(serve)是否生成CSS Sourcemap。默认开发环境不输出CSS sourcemap,浏览器调试面板看到编译后的CSS,无法直接映射回原始scss/less文件
javascript
const _cssConfigDefaults = Object.freeze({
transformer: 'postcss',
preprocessorMaxWorkers: true,
devSourcemap: false,
})
export const cssConfigDefaults =_cssConfigDefaults
多环境并行构建器默认配置:builderOptionsDefaults
Vite Experimental 多环境并行构建器(Builder) 配置默认模板,支撑多 Environment 并行构建(同时打包 client + ssr 等多个环境产物)
sharedConfigBuild:控制多个环境执行构建时,是否复用同一套构建上下文、共享构建配置缓存 。默认每个Environment构建拥有独立构建上下文;各个环境完整独立运行构建流程、独立初始化Rollup。隔离性更强,环境之间不会互相污染,但重复初始化逻辑,资源开销更高。开启后要求各个环境构建之间不存在冲突的全局状态,如果插件内部存在全局变量、非环境隔离缓存,开启后极易出现client/ssr构建互相串数据的bugsharedPlugins:多环境并行构建时,插件实例是否在多个Environment之间共享同一个引用 。默认为每一个Environment克隆一份全新插件实例 。契合Vite多环境设计理念,插件内部状态依靠perEnvironmentState做环境隔离,即使插件不慎使用顶层变量,多个环境之间不会互相干扰,安全性更高。开启时插件若使用顶层普通变量存储状态,缺少环境隔离,并行构建时会产生竞态、状态错乱
javascript
const _builderOptionsDefaults = Object.freeze({
sharedConfigBuild: false,
sharedPlugins: false,
})
export const builderOptionsDefaults = _builderOptionsDefaults
开发服务器默认配置:serverConfigDefaults
Vite 开发服务器(Dev Server)顶层默认配置模板,管理 vite serve 开发阶段 HTTP 服务、HMR、文件系统访问安全、代理、预热、中间件模式、多环境开发行为
port:开发服务器监听端口。默认5173,端口占用时自动递增寻找可用端口strictPort:端口严格模式。默认端口被占用时,自动顺延尝试下一个端口host:服务监听地址。默认localhost,仅本机可访问,局域网其他设备无法连接。设置0.0.0.0监听所有网卡,支持局域网手机 / 其他电脑访问allowedHosts:Host请求头白名单,防御DNS重绑定攻击。默认空数组,仅允许localhost、127.0.0.1等本地host。当请求Host不在允许列表,浏览器访问会被拦截,弹出安全提示。使用内网域名、隧道(frp/ngrok)访问开发服务时,需要配置域名https:开启HTTPS开发服务。默认关闭https。支持传入true(自动生成自签名证书)或者{key,cert}指定证书文件proxy:开发接口代理(http-proxy),转发/api等请求到后端服务。默认不启用任何代理cors:开发服务器跨域资源共享CORS配置。默认允许本地各类开发地址headers:给所有开发服务响应统一附加HTTP响应头。默认空对象,不追加自定义头;常用于添加安全响应头、跨域相关Headerwarmup:开发服务启动预热模块列表 ,服务启动初期提前加载、编译指定模块,避免首次打开页面大量模块编译卡顿。默认空数组,不主动预热任何模块clientFiles:浏览器客户端环境预热文件ssrFiles:SSR 服务端环境预热文件
middlewareMode:中间件模式开关。默认关闭,独立启动完整Vite HTTP Serverfs:文件系统安全配置strict:是否开启严格文件系统访问限制,只允许读取项目根目录、server.fs.allow白名单内的文件,禁止任意路径逃逸读取服务器本地文件。默认开启deny:黑名单,禁止客户端请求访问这些文件。默认拦截环境变量文件、密钥证书、git目录,防止敏感文件通过开发服务泄露allow:额外可读目录。默认空
preTransformRequests:是否开启请求预转换,开发服务器收到资源请求前,提前执行依赖预构建、模块转换逻辑。默认开启。关闭会降低开发请求响应速度,极少场景需要关闭perEnvironmentStartEndDuringDev:多环境架构实验性配置控制开发环境中,Environment环境是否独立启停 。默认所有环境共享同一个文件监听实例。开启后,每个Environment拥有独立文件监听,实现不同环境差异化处理文件变更事件。属于多Environment配套实验能力
javascript
const _serverConfigDefaults = Object.freeze({
port: DEFAULT_DEV_PORT,
strictPort: false,
host: 'localhost',
allowedHosts: [],
https: undefined,
open: false,
proxy: undefined,
cors: { origin: defaultAllowedOrigins },
headers: {},
warmup: {
clientFiles: [],
ssrFiles: [],
},
middlewareMode: false,
fs: {
strict: true,
deny: ['.env', '.env.*', '*.{crt,pem}', '**/.git/**'],
},
preTransformRequests: true,
perEnvironmentStartEndDuringDev: false,
perEnvironmentWatchChangeDuringDev: false,
})
export const serverConfigDefaults = _serverConfigDefaults
export const defaultAllowedOrigins =
/^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/
SSR 默认配置:ssrConfigDefaults
Vite 顶层 SSR 默认配置模板 ,通过 target 区分 Node / WebWorker SSR,指导后续模块解析、构建环境差异化分支,支持客户端与 SSR 两套独立预构建策略
target:指定SSR代码运行目标环境,决定模块解析、内置模块处理、打包策略。默认nodenode:SSR产物运行在标准Node.js服务环境,识别Node内置模块(fs、path、http),遵循Node模块解析规则webworker:SSR代码运行在浏览器WebWorker / Cloudflare Worker等非标准Node运行时,此时会调整解析策略,过滤部分Node内置模块、调整conditions、mainFields
optimizeDeps:SSR环境专属的依赖预构建覆盖配置 ,用于覆盖顶层optimizeDeps配置。默认空对象,不做任何覆盖,继承全局optimizeDeps
javascript
const _ssrConfigDefaults = Object.freeze({
target: 'node',
optimizeDeps: {},
})
export const ssrConfigDefaults = _ssrConfigDefaults
设计思想
关注点分离
之前 : dev server 和 build 共享同一个模块图、同一个插件上下文。HMR、SSR、build 的边界模糊。
之后: 每个环境有自己的:
moduleGraph--- 独立模块依赖追踪pluginContainer--- 独立插件实例链hot--- 独立HMR通道depsOptimizer--- 独立依赖优化策略
bash
┌──────────────┐
│ TopLevel │ root, base, command, server, css, json, ...
│ Config │
└──────┬───────┘
│
┌───────┼───────────┐
│ │ │
▼ ▼ ▼
┌────┐ ┌──────┐ ┌─────────┐
│ │ │ │ ssr │ │ custom │
│client│ │ │ │ │
└────┘ └──────┘ └─────────┘
每个环境: moduleGraph, plugins, hot, optimizer
配置继承(Proxy + 回退)
PartialEnvironment.config 使用 Proxy 实现两级查找:
- 环境级配置优先(
resolve.conditions、dev.warmup等) - 顶层配置回退(
root、base、server等)
不需要在每个环境中重复声明 root、command 等通用属性。
插件级环境感知
通过 perEnvironmentPlugin() + applyToEnvironment,插件可以:
- 选择在哪些环境中运行
- 在不同环境中使用不同的配置
- 在不同环境中返回不同的插件实现
状态隔离(WeakMap)
perEnvironmentState() 用 WeakMap<Environment, State> 实现环境隔离的状态存储。每个环境访问到的是自己的状态副本,互不干扰。环境销毁后状态自动 GC
可扩展性
用户可以自定义环境:
typescript
// vite.config.ts
export default {
environments: {
client: { ... },
ssr: { ... },
worker: { // 自定义环境
dev: {
createEnvironment(name, config, context) {
return new WorkerEnvironment(name, config, context)
},
},
build: {
createEnvironment(name, config, options) {
return new WorkerBuildEnvironment(name, config, options)
},
},
},
},
}
与 PluginContext 的融合
插件钩子中通过 this.environment 访问当前环境上下文:
typescript
const plugin = {
name: 'my-plugin',
transform(code, id) {
const env = this.environment // 对应当前处理的环境
console.log(env.name) // 'client' | 'ssr' ...
console.log(env.config.mode) // 'dev' | 'build'
}
}
这使得插件在不同环境中运行相同的 transform 钩子,但通过环境上下文区分行为。