Vitest deps 配置完全指南:依赖解析、预打包优化与 CJS 互操作
【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest
Vitest 的test.deps配置是控制测试运行器如何处理外部依赖的核心入口,它决定了依赖是否被预打包优化、资源文件与 CSS 如何处理、CommonJS 模块如何互操作,以及vi.mock如何定位模拟文件。本文基于 Vitest 官方配置文档并结合仓库源码,逐项拆解deps.optimizer、deps.client、deps.interopDefault与deps.moduleDirectories四组选项的语义、默认值、适用场景与底层实现,帮助你在真实项目中精准调优依赖处理行为。
deps 配置总览
deps是一个复合配置对象,类型为{ optimizer?, ... },专门负责**依赖解析(dependency resolution)**的处理策略。在仓库源码中,它的完整类型定义位于 packages/vitest/src/node/config/serializeConfig.ts 对应的类型文件 packages/vitest/src/node/types/config.ts:
interface DepsOptions { optimizer?: Partial<Record<'client' | 'ssr' | ({} & string), DepsOptimizationOptions>> web?: { transformAssets?: boolean transformCss?: boolean transformGlobPattern?: RegExp | RegExp[] } interopDefault?: boolean moduleDirectories?: string[] }版本说明:本文关联的官方文档以
deps.client指代「浏览器(client)环境下的资源处理选项」,而在当前仓库源码中,这一组选项在类型定义、配置序列化与运行时读取时统一使用deps.web命名(见 serializeConfig.ts 中的web: config.deps.web || {})。二者指向同一组配置语义,阅读源码时请注意这一命名差异。
该配置的默认值在 resolveConfig.ts 中统一补齐:
resolved.deps ??= {} resolved.deps.moduleDirectories ??= [] resolved.deps.optimizer ??= {} resolved.deps.optimizer.ssr ??= {} resolved.deps.optimizer.ssr.enabled ??= false resolved.deps.optimizer.client ??= {} resolved.deps.optimizer.client.enabled ??= false resolved.deps.web ??= {} resolved.deps.web.transformAssets ??= true resolved.deps.web.transformCss ??= true resolved.deps.web.transformGlobPattern ??= []可以看到deps的默认行为是:不开启依赖预打包(enabled均为false)、默认处理资源与 CSS 文件、默认启用 CJS 互操作。
deps.optimizer:依赖预打包优化
- 类型:
{ ssr?, client? } - 核心思想:将
include中列出的外部库通过 esbuild 打包成单个文件,并以整体模块的方式导入。
当你的测试很多时,开启依赖优化可能显著提升测试性能。Vitest 遇到include中列出的外部库时,会将其打包为一个文件整体导入,这带来几点好处:
- 减少导入开销:导入包含大量内部依赖的包非常昂贵,打包成一个文件可以节省大量时间;
- 解决 UI 库的运行环境问题:UI 库并非为在 Node.js 中运行而设计,预打包使其能被正确加载;
- alias 配置生效:你的
alias配置在被打包的依赖内部同样生效; - 更贴近浏览器运行行为:测试中的代码运行方式更接近真实浏览器环境。
使用前提与模式选择
需要特别注意的是,只有deps.optimizer?.[mode].include中列出的包才会被预打包(部分插件如 Svelte 会自动填充该列表)。Vitest 对 Vite 的依赖优化选项做了裁剪:不支持disable与noDiscovery两个选项,其余选项可参考 Vite 的 Dep Optimization Options 文档。
默认情况下,Vitest 对环境的映射规则如下:
| 测试环境 | 使用的优化配置 |
|---|---|
jsdom、happy-dom | optimizer.client |
node、edge | optimizer.ssr |
该选项还会继承你的optimizeDeps配置:对 Web 环境(client)扩展optimizeDeps,对 SSR 环境扩展ssr.optimizeDeps。如果你在deps.optimizer中重新定义了include/exclude,运行测试时它会在optimizeDeps的基础上进行扩展;同时 Vitest 会自动将exclude中出现的条目从include中移除,避免冲突。
调试时的注意事项
::: tip 开启预打包后,你将无法直接编辑node_modules中的代码进行调试——因为实际运行的代码位于cacheDir或test.cache.dir目录下。如果你需要用console.log调试,有两个选择:直接编辑缓存目录中的代码,或使用deps.optimizer?.[mode].force选项强制重新打包。 :::
deps.optimizer.{mode}.enabled
- 类型:
boolean - 默认值:
false
单独控制某个模式(client或ssr)是否启用依赖优化。需要与include列表配合使用才能生效。
import { defineConfig } from 'vitest/config' export default defineConfig({ test: { deps: { optimizer: { client: { enabled: true, include: ['lodash-es', 'some-ui-library'], }, ssr: { enabled: true, include: ['node-fetch'], }, }, }, }, })从实现看,序列化时 serializeConfig.ts 仅将每个模式的enabled标志透传给 worker,实际的打包与依赖图处理由 Vite 的优化管线完成。
deps.client(源码中为 deps.web):客户端资源处理
- 类型:
{ transformAssets?, ... }
这组选项作用于环境设置为client时的外部文件。默认情况下jsdom和happy-dom使用client环境,node与edge使用ssr环境,因此这些选项对后两者环境中的文件不产生影响。
通常node_modules中的文件会被外部化(externalize)处理,但这组选项同样会影响server.deps.external中列出的文件。
deps.client.transformAssets
- 类型:
boolean - 默认值:
true
控制 Vitest 是否像浏览器中的 Vite 那样处理资源文件(.png、.svg、.jpg等)并解析它们。当未指定 query 参数时,这类模块的默认导出等于资源文件的路径。
deps.client.transformCss
- 类型:
boolean - 默认值:
true
控制 Vitest 是否像浏览器中的 Vite 那样处理 CSS 文件(.css、.scss、.sass等)。如果 CSS 已被css选项禁用,该选项只会静默ERR_UNKNOWN_FILE_EXTENSION错误。
deps.client.transformGlobPattern
- 类型:
RegExp | RegExp[] - 默认值:
[]
用于匹配应该被转换的外部文件的正则表达式。默认情况下,node_modules内的文件会被外部化且不经过转换,除非它是 CSS 或资源文件且对应选项未被禁用。
运行时实现与池限制
::: warning 目前这组选项仅对vmThreads和vmForks两种池生效,使用默认的threads/forks池时这些选项不会起作用。 :::
从源码可以清晰看到其作用机制。在 packages/vitest/src/runtime/vm/vite-executor.ts 中,canResolve方法读取 worker 状态中的config.deps?.web配置,依次判断 CSS、资源与自定义正则模式:
public canResolve = (fileUrl: string): boolean => { if (fileUrl === CLIENT_FILE) { return true } const config = this.workerState.config.deps?.web || {} const [modulePath] = fileUrl.split('?') if (config.transformCss && CSS_LANGS_RE.test(modulePath)) { return true } if (config.transformAssets && KNOWN_ASSET_RE.test(modulePath)) { return true } if ( toArray(config.transformGlobPattern).some(pattern => pattern.test(modulePath), ) ) { return true } return false }即:当资源/CSS 文件或匹配transformGlobPattern的文件被请求时,Vitest 会将其交给 Vite 转换管线(走完整的转换与模块缓存),而不是按外部化模块直接以 Node 原生方式导入。这解释了为什么这些能力绑定在基于 VM 的池实现中。
deps.interopDefault:CJS 模块互操作
- 类型:
boolean - 默认值:
true
将 CJS 模块的default导出解释为命名导出。部分依赖只打包 CJS 格式且不提供 Node.js 能静态分析的命名导出。当你在 Node 环境下用import语法(而非require)导入这类依赖并使用命名导出时,会看到如下错误:
import { read } from 'fs-jetpack'; ^^^^ SyntaxError: Named export 'read' not found. The requested module 'fs-jetpack' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export.由于 Vitest 不做静态分析,无法在运行代码前失败,所以如果关闭该功能,你大概率会在测试运行时看到这类错误:
TypeError: createAsyncThunk is not a function TypeError: default is not a function默认情况下,Vitest 假定你使用打包器来规避这个问题,因此不会报错;但如果你的代码未经处理,可以手动关闭该行为:
import { defineConfig } from 'vitest/config' export default defineConfig({ test: { deps: { interopDefault: false, }, }, })从实现看,该选项通过配置序列化透传给执行器,在 packages/vitest/src/runtime/external-executor.ts 中用于外部模块执行时的默认导出处理,从而决定是否将 CJS 的module.exports整体作为default导出并展开为命名导出。
deps.moduleDirectories:模块目录解析
- 类型:
string[] - 默认值:
['node_modules']
一个应被当作模块目录的目录列表。该配置影响两处行为:
vi.mock的解析:当未提供 factory 且被 mock 的路径匹配某个moduleDirectories值时,Vitest 会在项目的 root 下查找__mocks__文件夹来解析 mock(详见vi.mock);- 依赖外部化判定:该选项还会影响一个文件是否应被当作模块进行外部化。默认情况下,Vitest 对
node_modules等模块目录中的依赖使用 Node 原生方式导入,绕过 Vite 的转换步骤。
设置该选项会覆盖默认值。如果你希望继续搜索node_modules,需要把它与其他选项一起列出:
import { defineConfig } from 'vitest/config' import path from 'node:path' export default defineConfig({ test: { deps: { moduleDirectories: ['node_modules', path.resolve('../../packages')], } }, })该配置在解析阶段会用于项目的模块解析上下文(参见 packages/vitest/src/node/resolver.ts),同时在项目配置传递时被读取(在 test/e2e/test/projects.test.ts 等端到端测试中可以看到project.config.deps.moduleDirectories的使用,用于验证多项目场景下的解析行为)。
小结:合理组合使用 deps 配置
| 配置项 | 默认值 | 核心作用 | 关键约束 |
|---|---|---|---|
deps.optimizer.{mode}.enabled | false | 开启依赖预打包提升性能 | 仅打包include中的库;不支持disable/noDiscovery |
deps.client.transformAssets | true | 像浏览器一样处理资源文件 | 仅vmThreads/vmForks池生效 |
deps.client.transformCss | true | 像浏览器一样处理 CSS 文件 | 仅vmThreads/vmForks池生效 |
deps.client.transformGlobPattern | [] | 用正则指定需转换的外部文件 | 仅vmThreads/vmForks池生效 |
deps.interopDefault | true | 将 CJSdefault解释为命名导出 | 关闭后对未处理的 CJS 依赖会运行时报错 |
deps.moduleDirectories | ['node_modules'] | 定义模块目录,影响vi.mock与外部化 | 设置即覆盖默认值,需手动保留node_modules |
实际项目中的常见组合是:测试量大时开启deps.optimizer并维护include白名单;在vmThreads/vmForks池下测试含资源导入的组件时,保持transformAssets/transformCss为true;遇到 CJS 依赖导入报错时优先排查interopDefault是否被误关闭;使用 monorepo 时通过moduleDirectories将本地 packages 目录纳入模块解析。理解这些选项的底层实现(配置解析、序列化与 VM 执行器),能让你在遇到依赖相关的疑难报错时快速定位根因。
【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考