Environment API 总述

Environment APIVite 7 引入的核心架构升级。它将之前单一的 dev/build 上下文拆分为多个独立的环境clientSSR、自定义),每个环境拥有独立的模块图、插件容器和 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 存在多 Environmentclient(浏览器环境)、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 初始化的默认基础配置模板函数

  • 遵循 "大部分复用、少量差异化覆盖" 原则definedevbuildresolve.aliasresolve.extensions 等全部继承,避免重复复制大量配置代码
  • 隔离「全局根配置」与「环境配置」的冲突字段 :主动清空 resolve.mainFields / resolve.conditionsVite 内部的 Client 环境、SSR 环境拥有各自独立默认的 mainFieldsconditions,不能直接复用顶层配置
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,根据 consumerclient/server)、环境类型自动推导隐式规则,输出一份结构完整、规则归一、校验通过的 EnvironmentOptions,供构造 Environment 实例使用

  • 判断是否为浏览器客户端环境,推导 consumer(消费端标识)
    • 用户显式传 options.consumer 优先使用
    • 未传入时自动推导:client环境 → consumer: 'client',其余(ssr)→ server
  • 标记当前是否是 SSR WebWorker 特殊环境 ,该环境会修改 keepProcessEnv、模块解析行为,作为后续分支判断条件
  • 安全校验逻辑(重点)
  • 调用子函数 resolveEnvironmentResolveOptions,解析模块解析配置 alias/extensions/mainFields/export conditions/preserveSymlinks 等,根据 consumerclient/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、导出条件 conditionsNode 内置模块处理、软链接等

  • 分层合并配置:系统默认 configDefaults.resolve → 环境自动推导默认 → 用户自定义覆盖:先填充环境专属智能默认,再接纳用户配置覆盖,兼顾开箱即用和可定制
  • 统一生成环境差异化模块解析规则 :集中管理 client / SSR / SSR WebWorker 三套包解析策略;一处收敛所有分支逻辑,Environment 不需要自行判断环境去设置 mainFields/conditions/builtinsSSR WebWorker 运行环境介于浏览器和 Node 之间,复用客户端包解析规则,但同时存在 noExternal 时禁用内置模块识别
    • mainFields 默认值
      • consumer 未定义或 consumer = clientSSR WebWorker 环境,使用 DEFAULT_CLIENT_MAIN_FIELDS(优先读取 browser 字段)
      • 标准 SSR 服务端,使用DEFAULT_SERVER_MAIN_FIELDS忽略 package.json browser 字段
    • conditionspackage.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 差异化默认行为 :集中一套规则,根据 consumerenvironmentName 自动填充开箱可用的默认值,减轻用户配置负担
    • sourcemapIgnoreList:默认使用内置函数判断路径是否属于 node_modulessourcemap 忽略 node_modules 内文件,不生成 / 不展示第三方包源码映射,缩减资源体积、提升调试体验
    • preTransformRequests:预转换请求开关优先级上层传入值 > 自动推导(consumer === client 开启预转换)。浏览器环境支持模块预转换,SSR 默认关闭预转换
    • createEnvironment:根据环境名称绑定对应的环境工厂函数,控制当前环境如何实例化开发阶段 Environment
      • clientdefaultCreateClientDevEnvironment(创建浏览器开发环境)
      • ssrdefaultCreateDevEnvironment(通用服务端开发环境)
    • 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

规范化、补齐默认值、处理兼容迁移,生成构建阶段 Environmentbuild 配置 ,只处理 build 相关选项,区分 consumer: client(浏览器产物)/ server(SSR产物);同时承担废弃配置迁移兼容polyfillModulePreload)、参数格式标准化、多字段联动推导

  • 统一三层合并范式 :全局默认 _buildEnvironmentOptionsDefaults → 环境智能推导默认值 → 用户自定义配置覆盖
  • 废弃 API 平滑迁移层 :集中处理 polyfillModulePreload 旧配置自动转换 + 警告;一处完成兼容,不需要插件、构建链路到处做兼容判断
  • 多类参数格式标准化
    • cssCodeSplit:非库模式开启 CSS 代码分割,lib: true(打包类库)默认关闭代码分割
    • minifySSR 构建 → 默认不压缩 falseclient 浏览器产物 → 默认 esbuild 压缩,标准化,统一格式,布尔 / 字符串归一
    • ssr:自动标记是否为 SSR 构建目标
    • emitAssetsClient 构建输出静态资源(图片、字体),SSR 构建默认不 emit 资源
    • createEnvironment:绑定构建环境工厂,统一创建 BuildEnvironment
    • modulePreload:多层格式归一化,支持 true / false / 对象三种写法统一转换
    • target:别名常量转换
    • cssTarget:自动兜底复用 target,用户不设置 CSS 编译目标,则复用顶层 targetJSCSS 构建目标保持一致
    • cssMinifySSR 服务端默认开启 css 压缩 esbuildClient 跟随 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
    • sourcemapIgnoreListsourcemap 忽略文件过滤函数,交由环境解析阶段重新赋值 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 白名单等默认值)
    • previewvite preview 预览 dist 产物服务器配置,默认端口 DEFAULT_PREVIEW_PORT(4173)
    • builder:实验性多包构建器配置(并行构建),独立常量
  • worker WebWorker 默认配置:WebWorker 默认打包格式 iife,worker 默认独立空插件列表
  • optimizeDeps 依赖预构建默认
    • disabled:构建阶段默认关闭依赖预构建(预构建仅用于开发环境)
    • force:默认不强制重建缓存
    • include:强制纳入预构建依赖,默认空
    • exclude:排除预构建,默认空
    • holdUntilCrawlEnd:等待模块扫描完成再启动服务,默认开启
    • needsInterop:强制对指定包开启 CJS 互操作包装,默认空
    • extensions:预构建扫描node_modules 内依赖时,识别哪些后缀视为可执行模块,用于解析第三方包内部引入文件,默认空
  • 顶层 SSR 相关默认配置 ssrssrConfigDefaults,控制 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 模块处理。默认 jscjs
  • dynamicImportVarsOptions:控制动态导入变量 import(./${name}.js) 的解析行为
    • warnOnError:动态导入分析失败是否输出警告。默认开启
    • exclude:不包括的文件。默认不扫描 node_modules 内的动态导入,提升构建速度、避免误解析第三方包
  • writeRollup 是否将产物写入磁盘。默认写入 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 渲染时需要启用
  • ssrEmitAssetsSSR 构建时是否一并输出静态资源(图片、字体等)。默认 SSR 构建只输出 JS,不输出资源。如果需要服务端同时托管静态资源,设置 true
  • reportCompressedSize:构建结束控制台是否输出 gzip /brotli 压缩后的体积统计。默认开启。CI 流水线想要精简日志可关闭
  • chunkSizeWarningLimitchunk 体积告警阈值(单位 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 / StylusCSS 预处理器 的多线程 Worker 数量,用于并行编译样式文件,提升开发构建速度。默认自动根据 CPU 核心数创建最大可用 workerVite 内部自动计算)
  • 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 监听所有网卡,支持局域网手机 / 其他电脑访问
  • allowedHostsHost 请求头白名单,防御 DNS 重绑定攻击。默认空数组,仅允许 localhost127.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
    • nodeSSR 产物运行在标准 Node.js 服务环境,识别 Node 内置模块(fspathhttp),遵循 Node 模块解析规则
    • webworkerSSR 代码运行在浏览器 WebWorker / Cloudflare Worker 等非标准 Node 运行时,此时会调整解析策略,过滤部分 Node 内置模块、调整 conditionsmainFields
  • optimizeDepsSSR 环境专属的依赖预构建覆盖配置 ,用于覆盖顶层 optimizeDeps 配置。默认空对象,不做任何覆盖,继承全局 optimizeDeps
javascript 复制代码
const _ssrConfigDefaults = Object.freeze({
  target: 'node',
  optimizeDeps: {},
})
export const ssrConfigDefaults = _ssrConfigDefaults

设计思想

关注点分离

之前 : dev serverbuild 共享同一个模块图、同一个插件上下文。HMRSSRbuild 的边界模糊。

之后: 每个环境有自己的:

  • 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.conditionsdev.warmup 等)
  • 顶层配置回退(rootbaseserver 等)

不需要在每个环境中重复声明 rootcommand 等通用属性。

插件级环境感知

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

相关推荐
至乐活着8 小时前
Vite 构建工具原理解析与实战:从 ES Module 到极速 HMR
vite·热更新·构建工具·前端工程化·es module
linsk199810 小时前
React18、19如何兼容 IE9、IE10
react·rollup·vite·兼容·ie
夕夕木各4 天前
从第一个 PR 到 Vite 官方中文文档维护者
github·vite
布兰妮甜5 天前
从 0 搭建企业级 Vue3/Vite 脚手架(规范、eslint、husky、打包、环境变量全流程)
typescript·vue3·vite·脚手架·前端工程化
HexCIer7 天前
面向未来的原子化 CSS:UnoCSS 核心架构分析与 Tailwind CSS 现状
前端·css·vite
触底反弹9 天前
🔥 保姆级教程|SSE + BFF + 跨域三件套,从零实现 ChatGPT 流式输出(附完整代码)
人工智能·node.js·vite
先吃饱再说9 天前
LLM 流式输出的“中间商”方案:BFF 层到底在做什么?
llm·vite
小林ixn10 天前
从BFF到SSE:我在Vue项目里藏了个“AI翻译官”
前端·vue.js·vite
还是大剑师兰特14 天前
Vite6 + Vue3 + TS 完整版标准配置模板
vite·大剑师·vite6配置