Environment API 总述
英文标题:Environment API 总述
Vite 7 引入的核心架构升级。它将之前单一的 dev/build 上下文拆分为多个独立的环境(client、SSR、自定义),每个环境拥有独立的模块图、插件容器、HMR 通道
5.4 KB
中文
Vite 7 引入的核心架构升级。它将之前单一的 dev/build 上下文拆分为多个独立的环境(client、SSR、自定义),每个环境拥有独立的模块图、插件容器、HMR 通道
中文全文翻译正在补齐,可先查看下方英文原文。
English / 原文
Environment API 是 Vite 7 引入的核心架构升级。它将之前单一的 dev/build 上下文拆分为多个独立的环境 (client、SSR、自定义),每个环境拥有独立的模块图、插件容器和 HMR 通道
Vite 6 及之前 Vite 7
┌─────────────────┐ ┌──────────────────────┐
│ ViteDevServer │ │ ViteDevServer │
│ ├─ moduleGraph │ │ ├─ moduleGraph (兼容)│
│ ├─ pluginCont. │ │ │ │
│ └─ watcher │ │ └─ environments │
└─────────────────┘ │ ├─ client │
│ │ ├─ moduleGraph
│ │ ├─ pluginContainer
│ │ └─ hot (HMR)
│ │
│ ├─ ssr
│ │ ├─ moduleGraph
│ │ ├─ pluginContainer
│ │ └─ hot (HMR)
│ │
│ └─ custom
│ └─ ...
└──────────────────────┘
完整的环境初始化流程
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) ← 预热文件
环境的生命周期
创建 (constructor)
│
▼
初始化 (init)
└─ 插件容器创建
└─ resolveEnvironmentPlugins → 环境级插件列表
│
▼
监听 (listen)
├─ HMR 启动
├─ 依赖预构建
└─ 文件预热
│
▼
运行中
├─ transformRequest(url) ← 按需转换
├─ reloadModule(module) ← HMR 触发
├─ fetchModule(id) ← ModuleRunner 获取
└─ waitForRequestsIdle() ← 等待爬取完成
│
▼
关闭 (close)
├─ 插件容器关闭
├─ 依赖优化器关闭
├─ HMR 通道关闭
└─ 等待 pending 请求完成
环境类继承层次
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,防止内存泄漏
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,不能直接复用顶层配置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]
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解析构建阶段配置
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 entireprocess.envobject todefine`, ' + '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,
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):明确约束,别名、软链接策略全局统一,禁止单个环境独立篡改,保证项目路径解析全局行为一致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|productionconst 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强制预构建策略优先级:顶层强制标记优先于默认配置,支持开发服务器全局触发重新预构建,忽略缓存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关闭过滤,内部统一标准化为函数类型,下游插件 / 转换逻辑无需重复类型判断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压缩
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 校验,默认关闭(安全校验开启)
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:构建产物输出目录。默认dist -
assetsDir:静态资源(图片、字体、媒体)在outDir下的子目录名称。默认assets,产物路径dist/assets/xxx.[hash].png -
assetsInlineLimit:资源内联阈值(单位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,不自动注入HTML -
ssrManifest:是否生成SSR资源清单ssr-manifest.json,清单用于服务端渲染,记录模块依赖关系、动态导入chunk映射。默认关闭;开启SSR渲染时需要启用 -
ssrEmitAssets:SSR构建时是否一并输出静态资源(图片、字体等)。默认SSR构建只输出JS,不输出资源。如果需要服务端同时托管静态资源,设置true -
reportCompressedSize:构建结束控制台是否输出gzip /brotli压缩后的体积统计。默认开启。CI流水线想要精简日志可关闭 -
chunkSizeWarningLimit:chunk体积告警阈值(单位kb)。默认500,当单个产物chunk超过500kb,控制台输出警告,提示进行代码分割优化 -
watch:构建监听模式(watch增量构建)配置。默认不开启watchconst _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文件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构建互相串数据的bug -
sharedPlugins:多环境并行构建时,插件实例是否在多个Environment之间共享同一个引用。默认为每一个Environment克隆一份全新插件实例 。契合Vite多环境设计理念,插件内部状态依靠perEnvironmentState做环境隔离,即使插件不慎使用顶层变量,多个环境之间不会互相干扰,安全性更高。开启时插件若使用顶层普通变量存储状态,缺少环境隔离,并行构建时会产生竞态、状态错乱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响应头。默认空对象,不追加自定义头;常用于添加安全响应头、跨域相关Header -
warmup:开发服务启动预热模块列表 ,服务启动初期提前加载、编译指定模块,避免首次打开页面大量模块编译卡顿。默认空数组,不主动预热任何模块clientFiles:浏览器客户端环境预热文件ssrFiles:SSR 服务端环境预热文件
-
middlewareMode:中间件模式开关。默认关闭,独立启动完整Vite HTTP Server -
fs:文件系统安全配置strict:是否开启严格文件系统访问限制,只允许读取项目根目录、server.fs.allow白名单内的文件,禁止任意路径逃逸读取服务器本地文件。默认开启deny:黑名单,禁止客户端请求访问这些文件。默认拦截环境变量文件、密钥证书、git目录,防止敏感文件通过开发服务泄露allow:额外可读目录。默认空
-
preTransformRequests:是否开启请求预转换,开发服务器收到资源请求前,提前执行依赖预构建、模块转换逻辑。默认开启。关闭会降低开发请求响应速度,极少场景需要关闭 -
perEnvironmentStartEndDuringDev:多环境架构实验性配置控制开发环境中,Environment环境是否独立启停。默认所有环境共享同一个文件监听实例。开启后,每个Environment拥有独立文件监听,实现不同环境差异化处理文件变更事件。属于多Environment配套实验能力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配置。默认空对象,不做任何覆盖,继承全局optimizeDepsconst _ssrConfigDefaults = Object.freeze({ target: 'node', optimizeDeps: {}, }) export const ssrConfigDefaults = _ssrConfigDefaults
设计思想
关注点分离
之前 : dev server 和 build 共享同一个模块图、同一个插件上下文。HMR、SSR、build 的边界模糊。
之后 : 每个环境有自己的:
-
moduleGraph— 独立模块依赖追踪 -
pluginContainer— 独立插件实例链 -
hot— 独立HMR通道 -
depsOptimizer— 独立依赖优化策略┌──────────────┐ │ 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
可扩展性
用户可以自定义环境:
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 访问当前环境上下文:
const plugin = {
name: 'my-plugin',
transform(code, id) {
const env = this.environment
console.log(env.name)
console.log(env.config.mode)
}
}
这使得插件在不同环境中运行相同的 transform 钩子,但通过环境上下文区分行为。