ToC
resolveConfig(inlineConfig, ‘serve’, ‘development’)
resolveConfig 方法内部先是保存了一些通过函数调用传递过来的参数,同时还根据 mode
参数的值设置 process\.env\.NODE_ENV
的值为 development
或是 production
:
查找对应的配置文件用到的方法是:loadConfigFromFile,它主要做的事情是根据预设的配置文件路径(也可能没有进行设置),在本机上查找对应的文件,读取并使用 esbuild
进行编译转换。
loadConfigFromFile(configEnv,configFile,config.root,config.logLevel)
在 loadConfigFromFile 文件中会找到配置文件的真实路径,如果启动的时候传入了目标文件位置,则使用目标位置查找,否则会使用启动项目时的根目录来分别尝试读取 'vite.config.js'
, 'vite.config.mjs'
, 'vite.config.ts'
, 'vite.config.cjs'
, 'vite.config.mts'
, 'vite.config.cts'
文件,因为是使用 for...of
方法来依次遍历的这个文件名列表,所以当文件名为 vite.config.js
读取最快,为 vite.config.cts
的时候最慢。读取文件是否存在时使用 fs.existsSync(filePath)
来进行判断的,如果不存在就会跳过本次循环,直到找到对应的配置文件。
如果最终没有找到文件则会在控制台打印:vite:config no config file found.
。如果找到了对应的配置文件则会获取这个文件的模块系统是 CommonJS 还是 ESModule,读取的方式为:
- 默认 isESM = false
- 如果文件以 mjs/mts 结尾,则文件是 ESM 格式
- 如果文件以 cjs/cts 结尾,则文件时 CJS 格式
- 如果以上方法都不适用,说明文件是以 js/ts 结尾,则通过通过 lookupFIle 查找到该项目的根目录并读取
package.json
文件内容,判断 .type === 'module'
,如果为 true 则表示是 ESM,反之亦然
为什么需要判断配置文件的格式呢?因为它决定了最终转换配置文件时输出的文件格式名和部分模块独有的全局变量(__dirname, __fileName, import.meta.url)是否能正产使用。接着会调用 bundleConbfigFile 方法传入 resolvePath(配置文件路径)和 isESM(是否是 ESModule),来将配置文件的代码用 esbuild 打包编译并替换模块独有的全局变量为静态变量值。
bundleConfigFile(resolvedPath, isESM)
在 esbuild 进行配置文件打包时,有两个自定义的 plugin,分别为:externalize-deps
、inject-file-scope-variables
,它们的作用是读取到 monorepo 中的一些共享依赖(或者称之为外部依赖),和注入静态变量(__dirname, __fileName, import.meta.url 在进行打包的时候会被 esbuild 打包时设置的 define 配置项中定义的变量名替换,而注入静态变量则是将这些静态变量名进行声明赋值,让它变成一个真实可用的变量)。
当然这里面涉及到了 esbuild plugin 的概念,在本文中不会对其详细展开,有机会再写一遍关于 esbuild plugin 的文章,这里只会对 vite 已经编写好的插件进行一个简单的解读,了解一下主要做了哪些事情。
最终将代码及文件的依赖关系导出:
result.metafile
属性大致是这样的:
在 bundleConfigFile 完成配置文件的转换后,会通过 loadConfigFromBundledFile 方法将配置文件动态加载进来,让其变成一个真正的 JavaScript 对象或函数(vite.config.js 也可以接收一个默认导出的函数作为配置,当导出一个函数的时候,参数是一个包含 mode、command、ssrBuild 的对象)。
loadConfigFromBundledFile(resolvedPath,bundled.code,isESM)
如果文件模块系统是 esm,写将代码写入一个临时文件,并使用 dynamicImport
方法,而 dynamicImport 方法则是一个普通函数,在函数内部返回这个文件:
至于为什么要这么做,在方法的注释上已经描述清楚了,为了不被 ts 和 rollup 编译掉。fileName
指的是配置文件在磁盘中的绝对路径(比如:/Volumes/code/vite/playground/html/vite.config.js),在创建临时文件后会尝试使用 await importZ()
来导入它,导入完成后将尝试删除它:
而 CJS 的模块导入则要更麻烦一点,
最后会返回带有 path
、config
、dependencies
字段的的对象。normalizePath
属性的主要作用是判断当前运行平台是否是 window,如果是则会将路径中的 \\
转换为 /
。
现在调用栈会回到 resolveConfig 函数中,但其实剩下的工作都是对各种边界的处理,以及对 plugins 的排序和过滤,在处理完所有情况后,会将所有的选项合并后返回出来,至此,createServer
内部的 resolveConfig
就完成了,接下来会处理 resolveHttpsConfig
的配置项,但其实这个并不关键,因为默认情况下,httpsOptions
都会是一个 undefined,除非指定了 https
参数。
所以我们下一篇来看一下 resolveChokidarOptions
、 resolveHttpServer
、createWebSocketServer
和创建 connect
中间件系统的处理。