Environment API 总述

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

源码文件:environment.ts

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

源码文件:config.ts

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

源码文件:config.ts

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

源码文件: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 解析构建阶段配置
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

源码文件: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):明确约束,别名、软链接策略全局统一,禁止单个环境独立篡改,保证项目路径解析全局行为一致
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

源码文件:config.ts

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

源码文件: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 关闭过滤,内部统一标准化为函数类型,下游插件 / 转换逻辑无需重复类型判断
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

源码文件: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 压缩
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

源码文件: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 校验,默认关闭(安全校验开启)
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

源码文件: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
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

源码文件: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 文件
javascript 复制代码
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 做环境隔离,即使插件不慎使用顶层变量,多个环境之间不会互相干扰,安全性更高。开启时插件若使用顶层普通变量存储状态,缺少环境隔离,并行构建时会产生竞态、状态错乱
javascript 复制代码
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 配套实验能力
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

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

相关推荐
向北丶11 天前
vite项目中配置alias别名
前端·vite
PedroQue9911 天前
修复 .uvue 页面路径残留问题
前端·vite
梦曦i11 天前
修复 .uvue 页面路径残留问题
前端·vite
cindershade15 天前
首屏在替谁付费:重做 Vite 产物治理的责任边界
vite
做前端的娜娜子16 天前
施工蓝图:vite.config.js —— 项目的指挥中心
前端·react native·vite
D_jing2017 天前
【踩坑解决】pnpm+Vite引入@koi/core报错spark-md5找不到模块(幽灵依赖深度解析)
pnpm·vite·幽灵依赖·前端踩坑
Flynt18 天前
pnpm 12 换上了 Rust 内核,我拿项目实测了一轮构建速度
rust·vite·前端工程化
xiaoyan201518 天前
2026最新款Electron41+React19+AntDesign电脑端后台管理系统Exe
react.js·electron·vite
梨想橙汁18 天前
Vite 优化、踩坑汇总 + Webpack 迁移 Vite 实战
前端·webpack·vite
猫不易19 天前
Webpack 与 Vite:从 Loader / Plugin 到 Rolldown 统一引擎
前端·vite