一文看懂 Vite 是如何加载 vite.config.ts 配置文件的?
“
❓ 有没有好奇过:
Vite 作为一个 npm 依赖库,它是如何加载我们项目中的vite.config.ts文件的?
你可能会觉得没什么吧,不就是通过文件路径 require 或 import 一下吗?或者使用readFile不也能读取到文件内容吗?
但是,如果你站在 Vite 库的开发者角度思考,你会发现,事情没有这么简单。
- 首先,
readFile肯定是不可以的。因为我们不是要拿到文件的内容,而是要执行里面的代码,并且拿到执行后的结果。 - 还需要考虑配置文件使用的是 cjs 还是 esm,以此来决定使用
require还是import来进行加载。 - 如果配置文件是 js 或者 JSON 类型,直接
require自然是没问题的;但是如果配置文件是 ts,那就没法直接require了。
这篇文章主要讨论配置文件是 ts 的情况。
先看一个 ts 的配置文件:
import { defineConfig } from 'vite';
export default defineConfig({
// ...
});对于 Vite 来说,vite.config.ts配置文件其实就是一个模块。只不过这个模块不是库内部的文件模块,而是通过用户提供的一个文件模块。
自然,要使用一个模块,当然是require这个模块就行了;如果是 ES Module,就使用import。
但是,由于 node 本身无法直接执行 ts 代码。因此,我们在执行这个模块代码之前,必须要先将此模块代码转成 js 代码,然后再require或import。
那么 Vite 是如何将 ts 转成 js 代码的呢?
从 Vite 官网可以找到这样一段话:

我们只需要关注第一句话:默认情况下,Vite 会使用 esbuild 将配置文件打包成一个临时文件,然后再加载该临时文件。
关键词:esbuild,我们前往 Vite 源码,就能看到:

其中build函数就是从esbuild库中导入的。它的作用就是将vite.config.ts配置文件编译并打包成一个js 模块,然后写入一个临时的 js 文件中并进行加载。
👉 我们可以写一个简化版的实现:
/**
* 导出一个定义配置文件的函数
*/
export function defineConfig(config: UserConfig) {
return config;
}
// 加载配置文件
export async funcrion loadConfig() {
const configFile = path.resolve('vite.config.ts');
const tmpFile = path.resolve('vite.config.js');
// 1. 编译打包
await esbuild.build({
entryPoints: [configFile],
platform: 'node',
format: 'cjs',
outfile: tmpFile,
});
// 2. 加载模块
const mod = require(tmpFile);
fs.rm(tmpFile);
return mod.default || mod;
}很简单,就是先编译,再加载。
也许强迫症的你会对产生的“临时文件”耿耿于怀,有没有什么办法不产生临时文件呢?
如果你使用的是cjs(上面的例子),那么恭喜你,是可以做到的。
// 加载配置文件
export async funcrion loadConfig() {
// 1. 编译打包
const result = await esbuild.build({
entryPoints: [path.resolve('vite.config.ts')],
platform: 'node',
format: 'cjs',
write: false,
});
// 2. 加载模块
const module = new Module('vite.config.ts');
module.paths = Module._nodeModulePaths(process.cwd());
module._compile(result.outputFiles[0].text, 'vite.config.ts');
return module.exports.default;
}如果是esm模块,网上说可以使用data URL实现(import('data:...')),这里就不去探讨了。
以上的方法都是基于esbuild来实现的,如果你觉得使用麻烦的话,那么你或许可以试试ts-node库。
// 加载配置文件
export async function loadConfig() {
const tsNode = await import('ts-node');
tsNode.register({
transpileOnly: true,
compilerOptions: {
module: 'commonjs'
}
});
const mod = await import(path.resolve('vite.config.ts'));
return mod.default ?? mod;
}ts-node这个库可以让你的 Nodejs 支持加载 ts 模块,它是在运行时动态将 ts 代码转译成 js,然后再交给 Node 来执行。
感谢阅读
👇️👇️👇️