中文资讯 掘金 AI Tue, 28 Jul 2026 正文 ok 3555 words

Environment API 总述

英文标题:Environment API 总述

Vite 7 引入的核心架构升级。它将之前单一的 dev/build 上下文拆分为多个独立的环境(client、SSR、自定义),每个环境拥有独立的模块图、插件容器、HMR 通道
打开原文相关 2 · 机会 2
media3.2 KB media589 B media5.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

源码文件:environment.ts

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

源码文件:config.ts

生成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

源码文件:config.ts

执行所有插件的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

源码文件:config.ts

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

源码文件:config.ts

规范化、填充默认值、生成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|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

源码文件:config.ts

生成补齐环境差异化默认、合并用户自定义配置的标准化 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

源码文件:config.ts

  • 统一三层合并范式 :全局默认 configDefaults.dev → 环境智能推导默认值 → 用户自定义配置覆盖

  • 规范化开发环境 (dev) 专属配置,自动区分 client/ssr 差异化默认行为:集中一套规则,根据 consumer、environmentName 自动填充开箱可用的默认值,减轻用户配置负担

    • sourcemapIgnoreList:默认使用内置函数判断路径是否属于 node_modules。sourcemap 忽略 node_modules 内文件,不生成 / 不展示第三方包源码映射,缩减资源体积、提升调试体验
    • preTransformRequests:预转换请求开关优先级上层传入值 > 自动推导(consumer === client 开启预转换)。浏览器环境支持模块预转换,SSR 默认关闭预转换
    • createEnvironment:根据环境名称绑定对应的环境工厂函数,控制当前环境如何实例化开发阶段 Environment
      • client → 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

源码文件:build.ts

规范化、补齐默认值、处理兼容迁移,生成构建阶段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:绑定构建环境工厂,统一创建 BuildEnvironment
    • modulePreload:多层格式归一化,支持 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

源码文件:config.ts

Vite 顶层根配置全局默认常量对象,存储 Vite UserConfig → ResolvedConfig 解析链路使用的全部内置默认配置基准 ,当用户 vite.config 缺少对应配置项时,以此对象字段作为兜底,同时作为 getDefaultEnvironmentOptions 等环境配置派生函数的数据源

  • 基础通用配置

    • define:全局变量注入,默认不注入任何 process.env、全局常量
    • base:应用部署基础路径,默认 /
    • publicDir:静态资源目录,默认 public
    • appType:默认按单页面应用处理路由、HTML 逻辑,可选 mpa / custom
    • envDir:环境变量文件存放目录,默认读取 root 根目录 .env 文件
    • envPrefix:环境变量白名单前缀,默认只暴露前缀 VITE_* 变量给客户端
    • logLevel/customLogger/clearScreen:日志与控制台,默认 info 级别,启动服务自动清屏
  • dev 开发基础默认配置

    • warmup:预预热模块列表,默认空
    • sourcemap:默认开启 JS sourcemap
    • sourcemapIgnoreList: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

源码文件:build.ts

构建环境(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 依赖转为 ESM

    • include:包括的文件。默认只转换 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 增量构建)配置。默认不开启 watch

    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

源码文件:css.ts

Vite css 全局子配置默认模板,定义开发环境 + 构建环境全部 CSS / 预处理器(Sass/Less/Stylus)处理流程

  • transformer:指定 CSS 转换处理器,负责自动前缀补全(autoprefixer)、嵌套语法、minify、现代 CSS 特性降级、postcss.config.js 插件执行。默认 postcss

    • postcss:兼容生态所有 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

源码文件:build.ts

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

源码文件:server.ts

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

源码文件:ssr.ts

Vite 顶层SSR 默认配置模板,通过 target 区分 Node / WebWorker SSR,指导后续模块解析、构建环境差异化分支,支持客户端与 SSR 两套独立预构建策略

  • target:指定 SSR 代码运行目标环境,决定模块解析、内置模块处理、打包策略。默认 node

    • node:SSR 产物运行在标准 Node.js 服务环境,识别 Node 内置模块(fs、path、http),遵循 Node 模块解析规则
    • webworker:SSR 代码运行在浏览器 WebWorker / Cloudflare Worker 等非标准 Node 运行时,此时会调整解析策略,过滤部分 Node 内置模块、调整 conditions、mainFields
  • optimizeDeps:SSR 环境专属的依赖预构建覆盖配置 ,用于覆盖顶层 optimizeDeps 配置。默认空对象,不做任何覆盖,继承全局 optimizeDeps

    const _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 钩子,但通过环境上下文区分行为。