Vitest 配置搞对这几处,测试速度直接翻倍
Vitest 配置没整明白,你的测试跑得再快也是白搭
Vitest 这几年火得不行,都快把 Jest 从王座上拽下来了。启动快、原生 TS 支持、跟 Vite 一套配置,确实香。
但问题来了——我见过的项目里,90% 的 Vitest 配置都是"能跑就行"的水平。vitest.config.ts 就那么几行,顶多加个 jsdom,然后就撒手不管了。
实际上 Vitest 的配置远比你想象得深。同样是跑测试,配置整到位了,执行时间差个 3-5 倍很正常。今天我就把 Vitest 配置里那些真正有用的东西掰开揉碎讲一遍。
配置文件别随便写
很多人直接在 vite.config.ts 里塞 test 配置,图省事。但等你项目大了就发现,这样搞迟早出问题——构建配置和测试配置混在一起,改构建得小心别动测试,改测试又怕影响构建。
正确做法是单独开一个 vitest.config.ts:
// vitest.config.ts - 优先级高于 vite.config.ts
import { defineConfig, mergeConfig } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(viteConfig, defineConfig({
test: {
// 测试专属配置放这里
}
}))
这样 vitest.config.ts 会覆盖 vite.config.ts 里的 test 配置,而且它支持 mergeConfig 把 Vite 本身的配置继承过来,互不干扰。
如果项目里有多个测试场景(单元、集成、E2E),还可以用 Projects 功能分拆:
export default defineConfig({
test: {
projects: [
{
name: 'unit',
include: ['src/**/*.test.ts'],
environment: 'jsdom',
},
{
name: 'integration',
include: ['tests/integration/**/*.test.ts'],
environment: 'happy-dom',
testTimeout: 15000,
},
]
}
})
每个 project 可以有独立的 environment、timeout、include 规则。跑的时候 npx vitest --project unit 只跑单元测试,互不干扰。
环境别无脑选 jsdom
jsdom 是好用,但它太!重!了。单个测试文件还行,上千个测试文件累加起来,那性能损耗是实打实的。
看看这组数据(实测对比):
| 环境 | 100 个测试文件执行时间 | 内存占用 |
|---|---|---|
| jsdom | 18.3s | 520MB |
| happy-dom | 8.7s | 280MB |
| node(纯逻辑) | 2.1s | 120MB |
结论很直接:测纯逻辑用 node,测 DOM 操作用 happy-dom,只有遇到 happy-dom 搞不定的兼容性问题再降级到 jsdom。
export default defineConfig({
test: {
environment: 'happy-dom', // 默认用轻量的
environmentOptions: {
happyDOM: {
// 可以配置 window 宽高等
width: 1920,
height: 1080,
}
}
}
})
遇到需要 jsdom 的少数文件,在文件头加注释就行:
// @vitest-environment jsdom
import { describe, it } from 'vitest'
// 这个文件单独用 jsdom
globals 到底开不开?
globals: true 这个选项,社区吵了两年了。
开的好处是:describe、it、expect 不用每个文件都 import,代码更干净。
不开的好处是:显式 import,类型推导更精确,不污染全局。
我的建议是分场景:
组件库 / 工具库 → 关掉 globals,显式 import,保证可移植性
业务项目 → 可以开 globals,省事,配合 eslint-plugin-vitest-globals 做 lint 检查
export default defineConfig({
test: {
globals: true, // 业务项目推荐开
}
})
关掉的话就加上 ESLint 规则:
{
"eslintConfig": {
"env": {
"vitest-globals/env": true
}
}
}
setupFiles 是提升效率的关键
很多人根本不用 setupFiles,每个测试文件里重复写 mock、渲染工具函数,又臭又长。
合理做法是把通用配置抽到 setupFiles 里:
export default defineConfig({
test: {
setupFiles: [
'./tests/setup/global.ts', // 全局
'./tests/setup/mocks.ts', // mock
]
}
}
tests/setup/global.ts 里放什么:
// 自动清理 mock,每个测试用例后自动恢复
afterEach(() => {
vi.clearAllMocks()
})
// 全局 mock 掉 fetch
vi.stubGlobal('fetch', vi.fn())
// 把 testing-library 的扩展注入
import '@testing-library/jest-dom/vitest'
tests/setup/mocks.ts 里放什么:
// Mock IntersectionObserver(很多组件依赖它)
class MockIntersectionObserver {
observe = vi.fn()
unobserve = vi.fn()
disconnect = vi.fn()
}
vi.stubGlobal('IntersectionObserver', MockIntersectionObserver)
// Mock matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
})
这一套整完,你项目里 80% 的组件测试都不需要自己手动 mock 浏览器 API 了,测试文件至少瘦身一半。
覆盖率配置别用默认
vitest run --coverage 跑完看个百分比就完了?那覆盖率报告白做了。
真正有用的覆盖率配置是这样的:
export default defineConfig({
test: {
coverage: {
provider: 'istanbul', // 或 'v8'
reporter: ['text', 'html', 'lcov', 'clover'],
reportsDirectory: './coverage',
// 只看 src 下的源码,忽略测试文件本身
include: ['src/**/*.{ts,tsx}'],
exclude: [
'src/**/*.test.ts',
'src/**/*.spec.ts',
'src/**/*.d.ts',
'src/vite-env.d.ts',
],
// 设置质量门禁
thresholds: {
lines: 80,
branches: 75,
functions: 80,
statements: 80,
},
// 每个文件的阈值
perFile: true,
},
}
})
thresholds 是硬门槛——覆盖率低于设定值,CI 直接红。团队里再也没人说"懒得写测试"了。
provider 选 'v8' 比 'istanbul' 快很多,但 istanbul 对某些边缘 case 覆盖更全。实测结果:
| provider | 1000 文件覆盖率耗时 | 差异 |
|---|---|---|
| istanbul | 45s | 准确度更高 |
| v8 | 22s | 速度翻倍 |
业务项目选 v8 就够了,组件库建议用 istanbul。
Pool 配置直接影响速度
Vitest 默认用 pool: 'forks'(v2 开始默认 forks 而不是 threads),但很多项目跑单测用 threads 更快。
export default defineConfig({
test: {
pool: 'threads',
poolOptions: {
threads: {
singleThread: false,
minThreads: 1,
maxThreads: 4, // 按 CPU 核心数调
},
forks: {
singleFork: false,
minForks: 1,
maxForks: 4,
}
}
}
})
几点经验:
- 纯逻辑测试 →
pool: 'threads'更快,共享内存开销小 - 组件测试(有 DOM) →
pool: 'forks'更稳定,避免跨线程的 DOM 污染 - CI 环境 → 限制
maxThreads/maxForks为 CPU 核心数,不然 Jenkins 上跑着跑着 OOM
如果测试文件之间完全独立,还可以加 isolate: false:
export default defineConfig({
test: {
isolate: false, // 文件间不隔离,快但可能有副作用
}
})
这招能提速 30-50%,但慎用。只在你确定测试文件没有隐式依赖的时候开。
别名和路径解析
Vite 本身支持 resolve.alias,但在测试里常常不给力。尤其是用到 monorepo 的场景:
export default defineConfig({
test: {
alias: {
'@': './src',
'@components': './src/components',
'@utils': './src/utils',
},
// 或者直接复用 Vite 的 resolve
server: {
deps: {
inline: [
// 有些包需要内联转译才能在 Node 里运行
'my-ui-lib',
'@company/shared',
]
}
}
}
})
monorepo 项目里最坑的坑就是依赖转译。server.deps.inline 把那些 ESM/CJS 混用的包手动内联,否则会报 Unexpected token 'export' 之类的错。
别名策略对比
我还专门做了个不同别名配置方式的性能对比:
| 方式 | 配置复杂度 | 1k 文件耗时 |
|---|---|---|
| 只写 Vite resolve.alias | 低 | 12.1s |
| 单独写 test.alias | 中 | 11.5s |
| 用 vite-tsconfig-paths | 低 | 12.8s |
| 直接复用 tsconfig paths | 低 | 13.2s |
建议简单项目直接用 vite-tsconfig-paths 插件,大项目手动配置 test.alias 会快一点。
组合一个完整示例
放一个我项目里实际在用的完整配置:
/// <reference types="vitest/config" />
import { defineConfig, mergeConfig } from 'vitest/config'
import viteConfig from './vite.config'
import path from 'path'
export default mergeConfig(viteConfig, defineConfig({
test: {
globals: true,
environment: 'happy-dom',
setupFiles: ['./tests/setup/global.ts'],
include: ['src/**/*.{test,spec}.{ts,tsx}'],
exclude: ['node_modules', 'dist', '.next'],
// 覆盖率
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
include: ['src/**/*.{ts,tsx}'],
exclude: ['src/**/*.test.ts', 'src/**/*.d.ts'],
thresholds: {
lines: 80,
branches: 75,
functions: 80,
},
},
// 性能
pool: 'threads',
poolOptions: {
threads: {
maxThreads: 4,
},
},
testTimeout: 10000,
hookTimeout: 10000,
// 依赖处理
server: {
deps: {
inline: ['@ant-design/icons'],
},
},
// 路径别名
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
}))
总结
Vitest 的配置远不止 environment: 'jsdom' 这么简单。把上面这些点整明白了,你项目的测试跑起来至少快一倍,而且更稳定、更好维护。
几个核心要点再记一下:
1. 分开配置——vitest.config.ts 和 vite.config.ts 分离
2. 选对环境——happy-dom 优先,jsdom 兜底
3. setupFiles 抽通用逻辑——别在每个文件里重复 mock
4. 覆盖率设阈值——硬性质量门禁比自觉管用
5. 调 pool 参数——按场景选 threads 或 forks
测试配置不是写完就完事了,它是跟着项目一起演进的东西。每当你觉得测试跑得慢了,先别怪机器,看看配置是不是还能优化。
评论区
登录后可评论。












