Vitest experimental 配置完全指南:OpenTelemetry、importDurations、viteModuleRunner 等实验性特性的开启与实战

Vitest experimental 配置完全指南:OpenTelemetry、importDurations、viteModuleRunner 等实验性特性的开启与实战 Vitest experimental 配置完全指南OpenTelemetry、importDurations、viteModuleRunner 等实验性特性的开启与实战【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest本篇技术指南以 Vitest 官方配置文档 docs/config/experimental.md 为骨架系统讲解test.experimental配置对象下 7 个实验性特性——openTelemetry、importDurations、viteModuleRunner、vcsProvider、nodeLoader、preParse与diagnostics的类型定义、默认值、CLI 等价写法、适用场景与已知限制。读完本文你将能够在实际项目中安全地开启这些实验特性并借助它们完成链路追踪、导入耗时分析、跳过 Vite 转换、自定义变更检测与性能瓶颈诊断等任务。所有配置项均定义在 Vitest 的类型声明 packages/vitest/src/node/types/config.ts 中并通过 packages/vitest/src/node/config/resolveConfig.ts 在启动时完成默认值填充与路径解析同时每个子命令都在 packages/vitest/src/node/cli/cli-config.ts 中有对应的 CLI 参数声明。需要注意的是实验性 API 的签名与行为可能在后续版本中发生变化升级 Vitest 时请留意各特性的版本标记。概览experimental 配置对象速查表配置项类型默认值引入版本一句话作用experimental.openTelemetryOpenTelemetryOptions{ enabled: false }4.0.11在主线程与每个测试文件前导入 OpenTelemetry SDK输出链路追踪数据experimental.importDurationsImportDurationsOptions{ print: false, failOnDanger: false, limit: 0, thresholds: { warn: 100, danger: 500 } }4.1.0采集并展示每个模块的导入耗时limit在print或 UI 启用时为 10experimental.viteModuleRunnerbooleantrue4.1.0是否使用 Vite 的 Module Runner 沙箱运行代码关闭则回退到原生importexperimental.vcsProviderVCSProvider \| stringgit4.1.1自定义--changed的变更文件检测提供者experimental.nodeLoaderbooleantrue4.1.0模块运行器关闭时是否用 Node Loader 转换文件以支持vi.mock等特性experimental.preParsebooleanfalse4.1.3运行前静态解析测试规格全局应用.only、-t、--tags-filter等experimental.diagnosticsboolean \| DiagnosticsOptionstrue5.0.0运行结束后输出可让测试显著加速的配置改进建议需要特别说明的是experimental.diagnostics的默认值配置解析代码在 resolveConfig.ts 中将其归一化为四个子开关全部为true的对象即{ isolate: true, environment: true, import: true, transform: true }。experimental.openTelemetry接入 OpenTelemetry 链路追踪版本标记4.0.11实验性openTelemetry用于控制 Vitest 的 OpenTelemetry 支持。当enabled为true时Vitest 会在主线程中以及每一个测试文件执行前导入你指定的 SDK 文件。interface OpenTelemetryOptions { enabled: boolean /** * 指向暴露 Node.js 版 OpenTelemetry SDK 的文件路径。 */ sdkPath?: string /** * 指向暴露浏览器版 OpenTelemetry SDK 的文件路径。 */ browserSdkPath?: string }基本用法sdkPath相对于项目的root解析必须指向一个以默认导出暴露已启动 SDK 实例的模块。一个最小可用的示例import { getNodeAutoInstrumentations } from opentelemetry/auto-instrumentations-node import { OTLPTraceExporter } from opentelemetry/exporter-trace-otlp-proto import { NodeSDK } from opentelemetry/sdk-node const sdk new NodeSDK({ serviceName: vitest, traceExporter: new OTLPTraceExporter(), instrumentations: [getNodeAutoInstrumentations()], }) sdk.start() export default sdkimport { defineConfig } from vitest/config export default defineConfig({ test: { experimental: { openTelemetry: { enabled: true, sdkPath: ./otel.js, }, }, }, })从源码看Vitest 在配置解析阶段会调用 Node 的resolve将sdkPath拼接为绝对路径再通过pathToFileURL转为file://URL见 resolveConfig.tsbrowserSdkPath同样会相对root解析但保留为普通路径字符串见 resolveConfig.ts。性能与使用注意事项性能警告OpenTelemetry 可能显著拖慢 Vitest建议仅用于本地调试文档原文为 PERFORMANCE CONCERNS 警告。未经 Vite 转换sdkPath指向的文件不会经过 Vitest 的转换管线Node 必须能直接处理其内容因此不要在其中使用 Vite 特有的语法或特性。完整的使用方式请参阅 docs/guide/open-telemetry.md。浏览器模式浏览器模式的接入请参考 OpenTelemetry 指南中的 Browser Mode 一节对应配置项为browserSdkPath。典型场景将 Vitest 与自定义上报服务配合定位是哪些测试或文件拖慢了测试套件。仓库中examples/opentelemetry目录提供了完整的接入示例含otel.js、otel-browser.js、docker-compose.yaml与 Jaeger 配置可直接对照参考。experimental.importDurations定位慢导入的模块加载耗时分析版本标记4.1.0实验性importDurations控制模块导入耗时的采集与展示用于定位哪个文件导入最慢。类型定义如下interface ImportDurationsOptions { /** * 何时在 CLI 终端打印导入耗时明细。 * - false: 从不打印默认 * - true: 总是打印 * - on-warn: 仅当某个导入超过 warn 阈值时打印 */ print?: boolean | on-warn /** * 若任何导入超过 danger 阈值则测试运行失败。 * 启用后一旦超阈值无论 print 设置如何都会打印明细。 * default false */ failOnDanger?: boolean /** * 采集并展示的导入数量上限。 */ limit?: number /** * 用于着色与告警的耗时阈值毫秒。 */ thresholds?: { /** 黄色/告警颜色阈值。default 100 */ warn?: number /** 红色/danger 颜色阈值同时驱动 failOnDanger。default 500 */ danger?: number } }默认值为{ print: false, failOnDanger: false, limit: 0, thresholds: { warn: 100, danger: 500 } }其中limit在print、failOnDanger或 UI 启用时会自动变为 10——这一逻辑由 resolveConfig.ts 实现只要三者任一为真shouldCollect即为 truelimit默认取 10。耗时口径Self 与 Total导入明细区分两个口径Self自身耗时导入该模块自身花费的时间不含其静态导入Total总耗时导入该模块花费的总时间包含其静态导入但不包含当前模块自身的transform时间。从运行时代码看采集逻辑位于 packages/vitest/src/runtime/runners/test.tsgetImportDurations()从workerState.moduleExecutionInfo中取出每个模块的执行时长按duration降序排序后截取前limit条得到包含selfTime、totalTime、external与importer的记录limit为 0 时直接返回空对象跳过采集。报告器端packages/vitest/src/node/reporters/base.ts只有在limit 0时才认为启用了模块耗时采集。print控制 CLI 打印时机类型boolean | on-warn默认false控制测试结束后是否在 CLI 终端打印导入明细。仅在default、verbose、tree三种 reporter 下生效相关 reporter 见 docs/guide/reporters.mdfalse从不打印true总是打印on-warn仅当有导入超过thresholds.warn时打印。Vitest UI 的Module Graph标签页可以始终切换导入明细展示不受print设置影响详情见 docs/guide/ui.md。CLI 中还支持布尔简写--experimental.importDurations会被转换为{ print: true }见 cli-config.ts 的transform逻辑。failOnDanger在 CI 中强制执行导入性能预算类型boolean默认false若任一导入超过thresholds.danger则整个测试运行以失败告终启用后一旦触发明细必定打印。这非常适合在 CI 中为导入性能设下硬性预算vitest --experimental.importDurations.failOnDangerlimit采集展示数量上限类型number默认0当print、failOnDanger或 UI 启用时为10限制 CLI 输出、Vitest UI 以及第三方 reporter 中采集与展示的导入条目数量。文件路径过长时Vitest 会从开头截断直到符合 45 字符的显示上限。thresholds着色与告警阈值类型{ warn?: number; danger?: number }默认{ warn: 100, danger: 500 }以毫秒为单位的耗时阈值warn黄色/告警颜色阈值默认 100msdanger红色/danger 颜色阈值同时作为failOnDanger的判据默认 500ms。另外Vitest UI 会在至少一个文件加载耗时超过danger阈值时自动展示导入明细。命令行完整写法示例vitest --experimental.importDurations.print --experimental.importDurations.limit 20 --experimental.importDurations.thresholds.warn 80 --experimental.importDurations.thresholds.danger 400仓库中的端到端测试 test/e2e/test/reporters/import-durations.test.ts 覆盖了该特性的 CLI 展示与着色行为可作为验证参考。experimental.viteModuleRunner跳过 Vite 转换直接运行版本标记4.1.0实验性类型boolean默认true控制 Vitest 是使用 Vite 的 Module Runner 会自动继承该值。从源码可见viteModuleRunner: false会走完全不同的执行路径worker 端在 packages/vitest/src/runtime/workers/base.ts 中跳过 Vite 模块运行器的初始化主线程侧在 packages/vitest/src/node/core.ts 与 packages/vitest/src/node/project.ts 中选择原生 runner同时 resolveConfig.ts 会直接拒绝istanbul覆盖率 viteModuleRunner: false的组合并抛出明确错误。何时应该关闭它如果测试运行环境与代码运行环境一致例如服务端后端代码或简单脚本可以考虑关闭模块运行器。不过文档仍然建议jsdom/happy-dom测试继续使用 Vite Module Runner 或直接在 browser 模式中运行因为后者无需额外配置。关闭该选项将禁用全部文件转换具体影响测试文件与源码不再经过 Vite 处理全局 setup 文件不再被处理自定义 runner/pool/environment 文件不再被处理配置文件仍由 Vite 处理因为这在 Vitest 知晓viteModuleRunner标志之前就已发生。此外需要注意当前 Vitest 仍依赖 Vite 提供模块图、watch 模式等功能该选项仅对forks或threads两种 pool 生效——packages/vitest/src/runtime/workers/vm.ts 会在vm系列 pool 下直接抛出错误提示改用threads或forks。Module Runnerinline 与 external 模块的划分默认情况下Vitest 在由 Vite Environment API 驱动的、非常宽松的模块运行器沙箱中执行测试。每个文件被划分为两类inline内联模块由 Module Runner 执行提供import.meta.env、require、__dirname、__filename、静态import以及自己的模块解析机制几乎零配置即可运行纯 JS 逻辑external外部模块以原生模式运行脱离模块运行器沙箱。在 Node.js 中这些文件通过原生import关键字导入并由 Node 直接处理。文档给出的权衡是在宽松的伪环境下跑 jsdom/happy-dom 测试可以理解但如果在非 Node.js 环境里跑 Node.js 测试可能会掩盖生产环境才暴露的错误——尤其是当你的代码不依赖 Vite 插件的转换时。已知限制与 mock 行为变化一些 Vitest 特性依赖文件转换Vitest 通过同步的 Node.js Loaders API 转换测试文件与 setup 文件来支持它们import.meta.vitest内联测试vi.mockvi.hoisted。这意味着上述特性至少需要 Node 22.15且当前在 Deno 与 Bun 中不可用。Vitest 只会在测试文件内部检测vi.mock与vi.hoisted导入的模块中它们不会被提升。若不用这些特性可设置experimental.nodeLoader: false关闭转换以提升性能若在非测试文件中使用它们则不会提升可能引发意外行为。由于viteModuleRunner取消了转换阶段以下特性会失效无import.meta.env改用process.env无 plugins没有转换阶段插件不生效可改用 customization hooks 注册无 alias别名不生效原因同上istanbul 覆盖率 provider 不可用改用v8配置解析时也会直接报错拦截vi.resetModules()不可用没有 API 使 ES 模块从模块缓存中失效。覆盖率支持现状当前只有文件能转换为 JavaScript 时v8provider 才可用。转换 TypeScript 时Vitest 使用module.stripTypeScriptTypesNode 22.13 提供。如果使用了自定义 module loader 中对viteModuleRunner false与isTransformedByVite的判断。Mock 相关行为变化ES 模块不支持属性覆盖因此如下写法会失效import * as fs from node:fs import { vi } from vitest vi.spyOn(fs, readFileSync).mockImplementation(() 42) // ❌但 Vitest 支持不覆盖实现的自动间谍化。当vi.mock传入spy: true时模块以保留原始实现的方式被 mock同时所有导出函数被包装进vi.fn()间谍import * as fs from node:fs import { vi } from vitest vi.mock(node:fs, { spy: true }) fs.readFileSync.mockImplementation(() 42) // ✅此外工厂 mock 基于顶层 await 实现因此被 mock 的模块无法在源码中通过require()加载vi.mock(node:fs, async (importOriginal) { return { ...await importOriginal(), readFileSync: vi.fn(), } }) const fs require(node:fs) // throws an error这是因为工厂可以是异步的。实际影响有限与 Vitest 默认行为一致node_modules内的内置模块不会被 mock。TypeScript 支持矩阵Node.js 22.18 / 23.6 及以上TypeScript 由 Node 原生转换无需额外配置Node.js 22.6–22.18可通过--experimental-strip-types标志启用原生 TS 支持NODE_OPTIONS--experimental-strip-types vitestNode.js 低于 22.6二选一——先构建测试文件与源码直接运行构建产物通过execArgv导入自定义 loader例如接入tsximport { defineConfig } from vitest/config const tsxApi import.meta.resolve(tsx/esm/api) export default defineConfig({ test: { execArgv: [ --importdata:text/javascript,import * as tsx from ${tsxApi};tsx.register(), ], experimental: { viteModuleRunner: false, }, }, })在 Deno 中运行时TypeScript 文件由运行时直接处理无需额外配置。仓库中的端到端测试 test/e2e/test/no-module-runner.test.ts 及其 fixture test/e2e/fixtures/no-module-runner/vitest.config.ts 是该模式可运行配置的现成参考。experimental.vcsProvider自定义变更文件检测版本标记4.1.1实验性类型VCSProvider | string默认git为--changed标志提供自定义的变更文件检测 provider。默认情况下 Vitest 使用 Git 检测变更文件实现VCSProvider接口即可接入其他版本控制系统interface VCSProvider { findChangedFiles(options: VCSProviderOptions): Promisestring[] } interface VCSProviderOptions { root: string changedSince?: string | boolean }内联对象写法import { defineConfig } from vitest/config export default defineConfig({ test: { experimental: { vcsProvider: { async findChangedFiles({ root, changedSince }) { // return paths of changed files return [] }, }, }, }, })模块路径写法也可以传入一个指向默认导出实现了VCSProvider接口的模块的字符串路径import { defineConfig } from vitest/config export default defineConfig({ test: { experimental: { vcsProvider: ./my-vcs-provider.js, }, }, })export default { async findChangedFiles({ root, changedSince }) { // return paths of changed files return [] }, }配置解析时非git的字符串路径会相对root解析为绝对路径见 resolveConfig.ts。仓库中的端到端测试 test/e2e/test/vcs-provider.test.ts 验证了自定义 provider 的接入流程。CLI 等价写法为--experimental.vcsProvider path。experimental.nodeLoader按需关闭 Node Loader 转换版本标记4.1.0实验性类型boolean默认true当模块运行器被禁用viteModuleRunner: false时Vitest 使用原生 Node.js module loader 转换文件以支持import.meta.vitest、vi.mock与vi.hoisted。如果你的代码不使用这些特性可以关闭它以提升性能。类型注释见 packages/vitest/src/node/types/config.ts特别说明该选项只影响loader.load方法Vitest 始终会定义loader.resolve来填充模块图。CLI 等价写法为--experimental.nodeLoader false。experimental.preParse运行前的静态规格解析版本标记4.1.3实验性类型boolean默认false在运行测试之前解析测试规格从而在不执行的情况下跨所有文件应用.only修饰符、-t测试名模式、--tags-filter、test lines 与 test IDs。例如若只有一个测试被标记.onlyVitest 将跳过所有文件中其余测试。主线程中的实现位于 packages/vitest/src/node/core.ts开启后先调用parseSpecifications填充每个 specification 的testModule再过滤掉task.mode skip的规格从而在真正执行前完成筛选。使用建议推荐在搭配使用.only、-t或--tags-filter时开启无条件开启可能因额外的解析步骤而拖慢测试运行。静态分析的局限重要警告预解析使用静态分析AST 解析而非执行测试文件因此测试名、标签与修饰符.only、.skip、.todo必须能被静态分析识别。动态测试名存于变量或由函数调用返回的名称与非字面量标签无法被正确解析// ✅ 可用 —— 静态字符串字面量 test(adds numbers, () {}) // ✅ 可用 —— 静态标签 test(my test, { tags: [unit] }, () {}) // ❌ 无法正确匹配 —— 动态名称 const name getName() test(name, () {}) // ❌ 无法正确匹配 —— 动态标签 const tags getTags() test(my test, { tags }, () {})experimental.diagnostics运行后的性能改进建议版本标记5.0.0实验性类型interface DiagnosticsOptions { /** * 当 isolate: true 为每个测试文件生成新 worker并重建环境 * 消耗大量时间时给出提示估算 isolate: false 可节省的时间。 * default true */ isolate?: boolean /** * 当为每个测试文件重建 DOM 环境占据主导、而 vm pool * 可在每个 worker 中只建一次时给出提示。 * default true */ environment?: boolean /** * 当测试文件反复求值同一模块图典型如 barrel 文件导入 * 且 isolate: false 可在每个 worker 中只求值一次时给出提示。 * default true */ import?: boolean /** * 当模块转换占据主导且 fsModuleCache 可跨运行持久化结果时给出提示。 * default true */ transform?: boolean }默认true解析时归一化为四个子开关全部开启见 resolveConfig.ts运行结束后当采集到的耗时数据表明某项配置调整能显著加速测试时Vitest 会打印性能提示例如Environment jsdom was created 40 times · 23.80s total, 79% of tracked time create it once per worker with pool: vmThreads (keeps per-file isolation) or isolate: false (shares it across files) learn more: https://vitest.dev/guide/improving-performance#test-environments重要行为约定提示永远不会建议修改显式设置的选项若配置里显式定义了pool则不会建议其他 pool显式配置的isolate永远不会被建议关闭提示在 CI 中同样会打印设为false可关闭全部提示也可单独关闭其中某一项若想实测某项配置调整的影响而非估算可运行vitest doctor。diagnostics.isolate类型boolean默认true当isolate: true为每个测试文件生成新 worker并重建环境消耗大量时间时给出提示估算isolate: false可节省的时间。复用 worker 也会让已求值的模块保持存活使文件不必重复求值共享的模块图。该估算的模块级耗时只在experimental.importDurations启用时才采集未启用时估算仅统计 worker 启动本身并以下界at least形式报告。报告器端实现见 packages/vitest/src/node/reporters/base.ts它先计算并行 lanes 下的 wall-clock 启动成本再结合启用了 importDurations 时各模块的selfTime重复求值收益最后通过isSavingWorthHinting判断是否值得提示。diagnostics.environment类型boolean默认true当为每个测试文件重建 DOM 环境占据主导、而vmpool 可在每个 worker 中只初始化一次环境时给出提示。diagnostics.import类型boolean默认true当测试文件反复求值同一模块图、而isolate: false可在每个 worker 中只求值一次时给出提示。这典型发生在 barrel 文件导入场景每个测试文件都通过一个 index 文件导入少量符号却求值了其背后整张模块图。重复程度按每个模块被提供给 worker 的次数衡量因此测试文件导入的多是互不相交模块的测试套件会保持安静——复用 worker 并不会减少它们的导入工作量。Import 837 modules were evaluated 16740 times · 15.69s total, 64% of tracked time ~850ms faster with isolate: false — shared modules are evaluated once per worker instead of once per file learn more: https://vitest.dev/guide/improving-performance#test-isolationdiagnostics.transform类型boolean默认true当模块转换占据主导时给出提示。没有持久化缓存时每次vitest run都会从零转换整张模块图fsModuleCache会把结果存入磁盘使重复运行跳过转换。该提示估算下次运行可节省的时间在 CI 中提示会附带说明缓存目录必须在多次运行之间持久化缓存才能生效。常见组合场景与最佳实践综合上述 7 个实验特性结合实际项目可以组合出如下典型用法本地性能调优开启experimental.importDurations配合print或 Vitest UI找出慢导入模块再结合experimental.diagnostics给出的isolate/environment/import/transform建议针对性调整 pool、isolate与fsModuleCacheCI 性能预算vitest --experimental.importDurations.failOnDanger在 CI 中强制执行导入耗时上限配合thresholds.danger自定义预算值纯 Node 环境轻量运行当测试代码不依赖 Vite 转换无插件、无 alias、无import.meta.env时关闭viteModuleRunner走原生import路径可显著减少环境开销同时用nodeLoader: false在不用vi.mock时进一步提速非 Git 仓库的增量测试实现VCSProvider接口内联对象或独立模块文件让--changed在 Mercurial 等 VCS 下正常工作大仓库精确筛选频繁使用.only、-t或--tags-filter时开启preParse在不执行的前提下全局应用筛选注意测试名与标签必须静态可分析。以上配置均可在 vitest.config 中通过test.experimental对象声明也可通过--experimental.key系列的 CLI 参数临时覆盖。由于这些特性仍处于实验阶段建议在升级 Vitest 主版本后重新核对该文档docs/config/experimental.md中的版本标记与默认值变化。【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考