Vi
Vitest 提供实用程序函数来帮助您完成其 vi
帮助程序。您可以全局访问它(当 全局配置 启用时),或者直接从 vitest
导入它
import { vi } from 'vitest'
模拟模块
本节介绍在 模拟模块 时可以使用 API。请注意,Vitest 不支持模拟使用 require()
导入的模块。
vi.mock
- 类型:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
将来自提供的 path
的所有导入模块替换为另一个模块。您可以在路径中使用配置的 Vite 别名。对 vi.mock
的调用被提升,因此您调用它的位置无关紧要。它始终在所有导入之前执行。如果您需要引用其范围之外的某些变量,您可以在 vi.hoisted
中定义它们,并在 vi.mock
中引用它们。
警告
vi.mock
仅适用于使用 import
关键字导入的模块。它不适用于 require
。
为了提升 vi.mock
,Vitest 会静态分析您的文件。它表明 vi
没有直接从 vitest
包中导入(例如,从某个实用程序文件中),因此无法使用。使用从 vitest
导入的 vi
使用 vi.mock
,或启用 globals
配置选项。
Vitest 不会模拟在 设置文件 中导入的模块,因为它们在测试文件运行时被缓存。您可以在 vi.hoisted
中调用 vi.resetModules()
,以在运行测试文件之前清除所有模块缓存。
如果定义了 factory
,所有导入都将返回其结果。Vitest 只调用一次工厂,并为所有后续导入缓存结果,直到调用 vi.unmock
或 vi.doUnmock
为止。
与 jest
不同,工厂可以是异步的。您可以使用 vi.importActual
或将工厂作为第一个参数传递的帮助程序,并在其中获取原始模块。
// when using JavaScript
.('./path/to/module.js', async () => {
const = await ()
return {
...,
// replace some exports
: .(),
}
})
// when using TypeScript
vi.mock('./path/to/module.js', async (importOriginal) => {
const mod = await importOriginal<typeof import('./path/to/module.js')>()
return {
...mod,
// replace some exports
namedExport: vi.fn(),
}
})
警告
vi.mock
被提升(换句话说,移动)到文件顶部。这意味着无论您何时编写它(无论是在 beforeEach
还是 test
中),它实际上都会在之前被调用。
这也意味着您无法在工厂中使用在工厂外部定义的任何变量。
如果您需要在工厂中使用变量,请尝试 vi.doMock
。它的工作方式相同,但不会被提升。请注意,它只模拟后续导入。
如果您需要在工厂中使用变量,请尝试 vi.doMock
。它的工作方式相同,但不会被提升。请注意,它只模拟后续导入。
import { namedExport } from './path/to/module.js'
const mocks = vi.hoisted(() => {
return {
namedExport: vi.fn(),
}
})
vi.mock('./path/to/module.js', () => {
return {
namedExport: mocks.namedExport,
}
})
vi.mocked(namedExport).mockReturnValue(100)
expect(namedExport()).toBe(100)
expect(namedExport).toBe(mocks.namedExport)
警告
如果您正在模拟具有默认导出的模块,则需要在返回的工厂函数对象中提供一个 default
键。这是一个 ES 模块特有的注意事项;因此,jest
文档可能有所不同,因为 jest
使用 CommonJS 模块。例如,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// etc...
}
})
如果在您要模拟的文件旁边有一个 __mocks__
文件夹,并且没有提供工厂,Vitest 将尝试在 __mocks__
子文件夹中找到具有相同名称的文件,并将其用作实际模块。如果您正在模拟依赖项,Vitest 将尝试在项目的 根目录 中找到一个 __mocks__
文件夹(默认值为 process.cwd()
)。您可以通过 deps.moduleDirectories 配置选项告诉 Vitest 依赖项的位置。
例如,您有以下文件结构
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
如果您在测试文件中调用 vi.mock
而不提供工厂,它将在 __mocks__
文件夹中找到一个文件用作模块
// increment.test.js
import { vi } from 'vitest'
// axios is a default export from `__mocks__/axios.js`
import axios from 'axios'
// increment is a named export from `src/__mocks__/increment.js`
import { increment } from '../increment.js'
vi.mock('axios')
vi.mock('../increment.js')
axios.get(`/apples/${increment(1)}`)
警告
请注意,如果您不调用 vi.mock
,模块不会自动模拟。要复制 Jest 的自动模拟行为,您可以在 setupFiles
中为每个所需的模块调用 vi.mock
。
如果没有 __mocks__
文件夹或提供的工厂,Vitest 将导入原始模块并自动模拟其所有导出。有关应用的规则,请参阅 算法。
vi.doMock
- 类型:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
与 vi.mock
相同,但它不会被提升到文件顶部,因此您可以引用全局文件范围内的变量。模块的下一个 动态导入 将被模拟。
警告
这不会模拟在此之前导入的模块。不要忘记,ESM 中的所有静态导入始终被 提升,因此将此放在静态导入之前不会强制它在导入之前被调用
vi.doMock('./increment.js') // this will be called _after_ the import statement
import { increment } from './increment.js'
// ./increment.js
export function increment(number) {
return number + 1
}
import { beforeEach, test } from 'vitest'
import { increment } from './increment.js'
// the module is not mocked, because vi.doMock is not called yet
increment(1) === 2
let mockedIncrement = 100
beforeEach(() => {
// you can access variables inside a factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }))
})
test('importing the next module imports mocked one', async () => {
// original import WAS NOT MOCKED, because vi.doMock is evaluated AFTER imports
expect(increment(1)).toBe(2)
const { increment: mockedIncrement } = await import('./increment.js')
// new dynamic import returns mocked module
expect(mockedIncrement(1)).toBe(101)
expect(mockedIncrement(1)).toBe(102)
expect(mockedIncrement(1)).toBe(103)
})
vi.mocked
- 类型:
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- 类型:
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
TypeScript 的类型帮助程序。只返回传递的对象。
当 partial
为 true
时,它将期望 Partial<T>
作为返回值。默认情况下,这只会让 TypeScript 相信第一级值被模拟。您可以传递 { deep: true }
作为第二个参数来告诉 TypeScript 整个对象都被模拟,如果它实际上是。
import example from './example.js'
vi.mock('./example.js')
test('1 + 1 equals 10', async () => {
vi.mocked(example.calc).mockReturnValue(10)
expect(example.calc(1, '+', 1)).toBe(10)
})
vi.importActual
- 类型:
<T>(path: string) => Promise<T>
导入模块,绕过所有检查,以确定它是否应该被模拟。如果您想部分模拟模块,这可能很有用。
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js')
return { ...axios, get: vi.fn() }
})
vi.importMock
- 类型:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
导入一个模块,其所有属性(包括嵌套属性)都被模拟。遵循与 vi.mock
相同的规则。有关应用的规则,请参阅 算法。
vi.unmock
- 类型:
(path: string) => void
从模拟注册表中删除模块。即使之前被模拟,所有导入调用都将返回原始模块。此调用被提升到文件顶部,因此它只会取消模拟在 setupFiles
中定义的模块,例如。
vi.doUnmock
- 类型:
(path: string) => void
与 vi.unmock
相同,但不会被提升到文件顶部。模块的下一个导入将导入原始模块而不是模拟。这不会取消模拟之前导入的模块。
// ./increment.js
export function increment(number) {
return number + 1
}
import { increment } from './increment.js'
// increment is already mocked, because vi.mock is hoisted
increment(1) === 100
// this is hoisted, and factory is called before the import on line 1
vi.mock('./increment.js', () => ({ increment: () => 100 }))
// all calls are mocked, and `increment` always returns 100
increment(1) === 100
increment(30) === 100
// this is not hoisted, so other import will return unmocked module
vi.doUnmock('./increment.js')
// this STILL returns 100, because `vi.doUnmock` doesn't reevaluate a module
increment(1) === 100
increment(30) === 100
// the next import is unmocked, now `increment` is the original function that returns count + 1
const { increment: unmockedIncrement } = await import('./increment.js')
unmockedIncrement(1) === 2
unmockedIncrement(30) === 31
vi.resetModules
- 类型:
() => Vitest
通过清除所有模块的缓存来重置模块注册表。这允许在重新导入时重新评估模块。顶级导入无法重新评估。可能有助于隔离模块,其中本地状态在测试之间发生冲突。
import { vi } from 'vitest'
import { data } from './data.js' // Will not get reevaluated beforeEach test
beforeEach(() => {
vi.resetModules()
})
test('change state', async () => {
const mod = await import('./some/path.js') // Will get reevaluated
mod.changeLocalState('new value')
expect(mod.getLocalState()).toBe('new value')
})
test('module has old state', async () => {
const mod = await import('./some/path.js') // Will get reevaluated
expect(mod.getLocalState()).toBe('old value')
})
警告
不会重置模拟注册表。要清除模拟注册表,请使用 vi.unmock
或 vi.doUnmock
。
vi.dynamicImportSettled
等待所有导入加载。如果您有一个同步调用,该调用开始导入您无法以其他方式等待的模块,则此方法很有用。
import { expect, test } from 'vitest'
// cannot track import because Promise is not returned
function renderComponent() {
import('./component.js').then(({ render }) => {
render()
})
}
test('operations are resolved', async () => {
renderComponent()
await vi.dynamicImportSettled()
expect(document.querySelector('.component')).not.toBeNull()
})
提示
如果在动态导入期间启动了另一个动态导入,则此方法将等待所有动态导入都解析。
此方法还将在导入解析后等待下一个setTimeout
滴答,因此所有同步操作都应在解析时完成。
模拟函数和对象
本节介绍如何使用方法模拟并替换环境和全局变量。
vi.fn
- 类型:
(fn?: Function) => Mock
在函数上创建一个间谍,尽管可以在没有函数的情况下启动。每次调用函数时,它都会存储其调用参数、返回值和实例。此外,您可以使用方法来操纵其行为。如果没有给出函数,则模拟将在调用时返回undefined
。
const = .(() => 0)
()
().()
().(0)
.(5)
const = ()
().(5)
().(2, 5)
vi.isMockFunction
- 类型:
(fn: Function) => boolean
检查给定参数是否为模拟函数。如果您使用的是 TypeScript,它也会缩小其类型。
vi.clearAllMocks
将对所有间谍调用.mockClear()
。这将清除模拟历史记录,但不会将其实现重置为默认实现。
vi.resetAllMocks
将对所有间谍调用.mockReset()
。这将清除模拟历史记录,并将其实现重置为空函数(将返回undefined
)。
vi.restoreAllMocks
将对所有间谍调用.mockRestore()
。这将清除模拟历史记录,并将其实现重置为原始实现。
vi.spyOn
- 类型:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
在对象的某个方法或 getter/setter 上创建一个间谍,类似于vi.fn()
。它返回一个模拟函数。
let = 0
const = {
: () => 42,
}
const = .(, 'getApples').(() => )
= 1
(.()).(1)
().()
().(1)
提示
您可以在vi.restoreAllMocks
内调用afterEach
(或启用test.restoreMocks
)以将所有方法恢复为其原始实现。这将恢复原始对象描述符,因此您将无法更改方法的实现
const cart = {
getApples: () => 42,
}
const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10)
console.log(cart.getApples()) // 10
vi.restoreAllMocks()
console.log(cart.getApples()) // 42
spy.mockReturnValue(10)
console.log(cart.getApples()) // still 42!
vi.stubEnv 0.26.0+
- 类型:
(name: string, value: string) => Vitest
更改process.env
和import.meta.env
上环境变量的值。您可以通过调用vi.unstubAllEnvs
来恢复其值。
import { vi } from 'vitest'
// `process.env.NODE_ENV` and `import.meta.env.NODE_ENV`
// are "development" before calling "vi.stubEnv"
vi.stubEnv('NODE_ENV', 'production')
process.env.NODE_ENV === 'production'
import.meta.env.NODE_ENV === 'production'
// doesn't change other envs
import.meta.env.MODE === 'development'
提示
您也可以通过简单地将其赋值来更改值,但您将无法使用vi.unstubAllEnvs
来恢复先前值
import.meta.env.MODE = 'test'
vi.unstubAllEnvs 0.26.0+
- 类型:
() => Vitest
恢复所有使用vi.stubEnv
更改的import.meta.env
和process.env
值。当它第一次被调用时,Vitest 会记住原始值并将其存储,直到再次调用unstubAllEnvs
。
import { vi } from 'vitest'
// `process.env.NODE_ENV` and `import.meta.env.NODE_ENV`
// are "development" before calling stubEnv
vi.stubEnv('NODE_ENV', 'production')
process.env.NODE_ENV === 'production'
import.meta.env.NODE_ENV === 'production'
vi.stubEnv('NODE_ENV', 'staging')
process.env.NODE_ENV === 'staging'
import.meta.env.NODE_ENV === 'staging'
vi.unstubAllEnvs()
// restores to the value that were stored before the first "stubEnv" call
process.env.NODE_ENV === 'development'
import.meta.env.NODE_ENV === 'development'
vi.stubGlobal
- 类型:
(name: string | number | symbol, value: unknown) => Vitest
更改全局变量的值。您可以通过调用vi.unstubAllGlobals
来恢复其原始值。
import { } from 'vitest'
// `innerWidth` is "0" before calling stubGlobal
.('innerWidth', 100)
=== 100
. === 100
// if you are using jsdom or happy-dom
. === 100
提示
您也可以通过简单地将其赋值给globalThis
或window
(如果您使用的是jsdom
或happy-dom
环境)来更改值,但您将无法使用vi.unstubAllGlobals
来恢复原始值
globalThis.innerWidth = 100
// if you are using jsdom or happy-dom
window.innerWidth = 100
vi.unstubAllGlobals 0.26.0+
- 类型:
() => Vitest
恢复globalThis
/global
(以及window
/top
/self
/parent
,如果您使用的是jsdom
或happy-dom
环境)上所有使用vi.stubGlobal
更改的全局值。当它第一次被调用时,Vitest 会记住原始值并将其存储,直到再次调用unstubAllGlobals
。
import { vi } from 'vitest'
const Mock = vi.fn()
// IntersectionObserver is "undefined" before calling "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock)
IntersectionObserver === Mock
global.IntersectionObserver === Mock
globalThis.IntersectionObserver === Mock
// if you are using jsdom or happy-dom
window.IntersectionObserver === Mock
vi.unstubAllGlobals()
globalThis.IntersectionObserver === undefined
'IntersectionObserver' in globalThis === false
// throws ReferenceError, because it's not defined
IntersectionObserver === undefined
伪计时器
本节介绍如何使用伪计时器。
vi.advanceTimersByTime
- 类型:
(ms: number) => Vitest
此方法将调用每个已启动的计时器,直到指定的毫秒数过去或队列为空 - 以先发生者为准。
let = 0
(() => .(++), 50)
.(150)
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- 类型:
(ms: number) => Promise<Vitest>
此方法将调用每个已启动的计时器,直到指定的毫秒数过去或队列为空 - 以先发生者为准。这将包括异步设置的计时器。
let = 0
(() => .().(() => .(++)), 50)
await .(150)
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- 类型:
() => Vitest
将调用下一个可用的计时器。在每次计时器调用之间进行断言很有用。您可以链接调用它来自己管理计时器。
let = 0
(() => .(++), 50)
.() // log: 1
.() // log: 2
.() // log: 3
vi.advanceTimersToNextTimerAsync
- 类型:
() => Promise<Vitest>
将调用下一个可用的计时器,并在其异步设置时等待其解析。在每次计时器调用之间进行断言很有用。
let = 0
(() => .().(() => .(++)), 50)
await .() // log: 1
(.).(1)
await .() // log: 2
await .() // log: 3
vi.getTimerCount
- 类型:
() => number
获取等待计时器的数量。
vi.clearAllTimers
删除所有计划运行的计时器。这些计时器将来永远不会运行。
vi.getMockedSystemTime
- 类型:
() => Date | null
返回使用setSystemTime
设置的模拟当前日期。如果日期未被模拟,则该方法将返回null
。
vi.getRealSystemTime
- 类型:
() => number
当使用vi.useFakeTimers
时,Date.now
调用会被模拟。如果您需要以毫秒为单位获取真实时间,则可以调用此函数。
vi.runAllTicks
- 类型:
() => Vitest
调用由process.nextTick
排队的每个微任务。这也会运行所有由它们自己安排的微任务。
vi.runAllTimers
- 类型:
() => Vitest
此方法将调用每个已启动的计时器,直到计时器队列为空。这意味着在runAllTimers
期间调用的每个计时器都会被触发。如果您有一个无限间隔,它将在 10 000 次尝试后抛出(可以使用fakeTimers.loopLimit
配置)。
let = 0
(() => .(++))
const = (() => {
.(++)
if ( === 3)
()
}, 50)
.()
// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
- 类型:
() => Promise<Vitest>
此方法将异步调用每个已启动的计时器,直到计时器队列为空。这意味着在runAllTimersAsync
期间调用的每个计时器都会被触发,即使是异步计时器也是如此。如果您有一个无限间隔,它将在 10 000 次尝试后抛出(可以使用fakeTimers.loopLimit
配置)。
(async () => {
.(await .('result'))
}, 100)
await .()
// log: result
vi.runOnlyPendingTimers
- 类型:
() => Vitest
此方法将调用在vi.useFakeTimers
调用后启动的每个计时器。它不会触发在调用期间启动的任何计时器。
let = 0
(() => .(++), 50)
.()
// log: 1
vi.runOnlyPendingTimersAsync
- 类型:
() => Promise<Vitest>
此方法将异步调用在 vi.useFakeTimers
调用后启动的所有计时器,即使是异步计时器。它不会触发在其调用期间启动的任何计时器。
(() => {
.(1)
}, 100)
(() => {
.().(() => {
.(2)
(() => {
.(3)
}, 40)
})
}, 10)
await .()
// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
- 类型:
(date: string | number | Date) => void
如果启用了假计时器,此方法模拟用户更改系统时钟(将影响与日期相关的 API,如 hrtime
、performance.now
或 new Date()
) - 但是,它不会触发任何计时器。如果未启用假计时器,此方法只会模拟 Date.*
调用。
如果您需要测试任何依赖于当前日期的内容,此方法很有用 - 例如,您代码中的 Luxon 调用。
const = new (1998, 11, 19)
.()
.()
(.()).(.())
.()
vi.useFakeTimers
- 类型:
(config?: FakeTimerInstallOpts) => Vitest
要启用模拟计时器,您需要调用此方法。它将包装对计时器的所有后续调用(例如 setTimeout
、setInterval
、clearTimeout
、clearInterval
、setImmediate
、clearImmediate
和 Date
),直到调用 vi.useRealTimers()
。
在使用 --pool=forks
通过 node:child_process
在 Vitest 内部运行时,不支持模拟 nextTick
。NodeJS 在 node:child_process
中内部使用 process.nextTick
,并在模拟时挂起。在使用 --pool=threads
运行 Vitest 时,支持模拟 nextTick
。
该实现基于内部 @sinonjs/fake-timers
。
提示
从版本 0.35.0
开始,vi.useFakeTimers()
不再自动模拟 process.nextTick
。它仍然可以通过在 toFake
参数中指定选项来模拟:vi.useFakeTimers({ toFake: ['nextTick'] })
。
vi.isFakeTimers 0.34.5+
- 类型:
() => boolean
如果启用了假计时器,则返回 true
。
vi.useRealTimers
- 类型:
() => Vitest
当计时器运行完毕时,您可以调用此方法将模拟计时器返回到其原始实现。所有之前安排的计时器都将被丢弃。
其他
Vitest 提供的一组有用的辅助函数。
vi.waitFor 0.34.5+
- 类型:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
等待回调成功执行。如果回调抛出错误或返回拒绝的 Promise,它将继续等待,直到成功或超时。
当您需要等待某些异步操作完成时,这非常有用,例如,当您启动服务器并需要等待它启动时。
import { expect, test, vi } from 'vitest'
import { createServer } from './server.js'
test('Server started successfully', async () => {
const server = createServer()
await vi.waitFor(
() => {
if (!server.isReady)
throw new Error('Server not started')
console.log('Server started')
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
)
expect(server.isReady).toBe(true)
})
它也适用于异步回调
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest'
import { getDOMElementAsync, populateDOMAsync } from './dom.js'
test('Element exists in a DOM', async () => {
// start populating DOM
populateDOMAsync()
const element = await vi.waitFor(async () => {
// try to get the element until it exists
const element = await getDOMElementAsync() as HTMLElement | null
expect(element).toBeTruthy()
expect(element.dataset.initialized).toBeTruthy()
return element
}, {
timeout: 500, // default is 1000
interval: 20, // default is 50
})
expect(element).toBeInstanceOf(HTMLElement)
})
如果使用 vi.useFakeTimers
,vi.waitFor
会在每个检查回调中自动调用 vi.advanceTimersByTime(interval)
。
vi.waitUntil 0.34.5+
- 类型:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
这类似于 vi.waitFor
,但如果回调抛出任何错误,执行将立即中断并收到错误消息。如果回调返回假值,则下一个检查将继续,直到返回真值。当您需要等待某些东西存在才能采取下一步操作时,这很有用。
查看下面的示例。我们可以使用 vi.waitUntil
等待元素出现在页面上,然后我们可以对元素执行某些操作。
import { , , } from 'vitest'
('Element render correctly', async () => {
const = await .(
() => .('.element'),
{
: 500, // default is 1000
: 20, // default is 50
}
)
// do something with the element
(.('.element-child')).()
})
vi.hoisted 0.31.0+
- 类型:
<T>(factory: () => T) => T
ES 模块中的所有静态 import
语句都提升到文件顶部,因此在导入之前定义的任何代码实际上将在评估导入后执行。
但是,在导入模块之前调用某些副作用(如模拟日期)可能很有用。
要绕过此限制,您可以将静态导入重写为动态导入,如下所示
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
在运行 vitest
时,您可以使用 vi.hoisted
方法自动执行此操作。
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
此方法返回从工厂返回的值。如果您需要轻松访问本地定义的变量,可以在 vi.mock
工厂中使用该值
import { expect, vi } from 'vitest'
import { originalMethod } from './path/to/module.js'
const { mockedMethod } = vi.hoisted(() => {
return { mockedMethod: vi.fn() }
})
vi.mock('./path/to/module.js', () => {
return { originalMethod: mockedMethod }
})
mockedMethod.mockReturnValue(100)
expect(originalMethod()).toBe(100)
请注意,即使您的环境不支持顶级 await,此方法也可以异步调用
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts')
return response.json()
})
vi.setConfig
- 类型:
RuntimeConfig
更新当前测试文件的配置。此方法仅支持会影响当前测试文件的配置选项
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// supports the whole object
},
maxConcurrency: 10,
sequence: {
hooks: 'stack'
// supports only "sequence.hooks"
}
})
vi.resetConfig
- 类型:
RuntimeConfig
如果之前调用了 vi.setConfig
,这将重置配置为原始状态。