Vitest 原生加载报 __vi_import_0__ 未定义,检查字符串里的 vi.mock(
测试里没有真正的 mock,字符串或注释却触发错误导入改写。该路径与 experimental.viteModuleRunner:false 有关。
测试只是读取一段包含 vi.mock( 的文本,却在正常导入处报 ReferenceError: __vi_import_0__ is not defined。若配置了 experimental.viteModuleRunner: false,检查这段字符串或注释有没有触发原生 loader 的 mock 预扫描。
Vitest 5.0.3 的明确条件是:文件含 vi.mock( 文本,没有真正的可提升 mock 调用,并导入、使用其他模块。例如下面的结构,./add.js 代表项目已有导出 add 的文件:
import { expect, test } from 'vitest';
import { add } from './add.js';
const fixture = 'vi.mock("./add.js")';
test('add', () => {
expect(add(1, 2)).toBe(3);
expect(fixture).toContain('vi.mock(');
});
这里 fixture 是测试数据,不能因为出现 vi.mock 就把它当成实际执行的 mock。
中间改写没有被丢弃
mock 提升器为了分析引用,可能先把导入变量改写成 __vi_import_0__ 一类内部名称;分析结束发现没有真实 mock 时,会返回未产生转换结果。正常 Vite 插件路径会据此放弃中间编辑,受影响的原生 loader 却仍使用已编辑的文本,留下引用而没有配套导入初始化。
这与真实 mock 工厂提前读取外部变量不同,也不同于静态类字段的同名识别问题。先保留原文件、配置和完整异常,确认确实走原生加载路径,再区分两类错误。
可以先恢复 module runner
项目允许回到 Vite 转换模型时,在原配置里明确启用:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
experimental: {
viteModuleRunner: true,
},
},
});
它影响整条测试模块加载链,不是单个字符串的替换。原生 Node 与 Vite module runner 对别名、插件转换、CommonJS 等行为有差异;原项目若有意验证原生运行语义,需要先评估这项变化。
必须保留原生路径时,可以只调整与业务语义无关的注释或非必要夹具写法,避免预扫描文本;但测试本来就需要验证逐字的 vi.mock( 内容时,不应改掉输入来让测试变绿。更适合保留最小失败文件,采用经核对的正式修复后回归。
已核对的 Vitest 5.0.5 原生 loader 仍直接调用 hoistMocks,没有用返回值丢弃这组中间编辑。因此不能只把 5.0.3 更新到 5.0.5 就承诺恢复,也不能把开放修改当成已交付补丁。
验收应同时覆盖普通导入、含 vi.mock 文本的夹具、真正的 mock,以及项目使用的 in-source test。确认断言真的执行、真实 mock 仍按预期替换。回退时恢复加载选项与锁文件,避免测试只在另一种模块语义下通过。


