从上篇文章可以知道,vite
在 dev
环境启动了一个服务器。这篇文章主要分析如何处理相关的配置信息。
信息源
vite
的配置信息有两个方式配置:
- 命令行参数
- 配置文件
- 插件
这里不具体讨论配置的具体参数,如何配置可以在 vite
官方文档中查看。
这篇文章从运行 vite
命令开始,查看配置数据的流转。
js
"scripts": {
"dev": "vite",
}
初始化
命令行参数
在上篇文章中,指出 vite
是通过 createServer
函数创建的 server
服务。这个函数接收一个参数,叫做 inlineConfig
,这个参数在执行vite
命令时,就是由命令行参数转换而来(也接收 vite
处理过的最终配置)。
js
const server = await createServer({
root,
base: options.base,
mode: options.mode,
configFile: options.config,
configLoader: options.configLoader,
logLevel: options.logLevel,
clearScreen: options.clearScreen,
server: cleanGlobalCLIOptions(options),
forceOptimizeDeps: options.force,
})
这里的 options
就是解析的命令行参数,由于当前并没有传入,所有为空对象,由此,最终传入 createServer
函数的参数对象的值都为 undefined
。
yaml
{
base: undefined
clearScreen: undefined
configFile: undefined
configLoader: undefined
forceOptimizeDeps: undefined
logLevel: undefined
mode: undefined
root: undefined
server: {}
}
如果传入命令行参数,则会被解析到具体的配置中。
最终配置
上面传入的配置数据,会被透传到内部函数 _createServer
,这个函数接收两个参数:
inlineConfig
:vite
配置options
: 函数选项
在这个函数中,做的第一件事就是处理配置。
js
const config = isResolvedConfig(inlineConfig)
? inlineConfig
: await resolveConfig(inlineConfig, 'serve')
核心就是以上代码,判断传入配置是否为最终配置,如果是就直接用,不是就调用 resolveConfig
函数进行处理。
resolveConfig
javascript
async function resolveConfig(
/** "内联配置":调用方以对象形式传进来的 Vite 配置(等价于 CLI 参数/配置文件里的同名项,会覆盖配置文件) */
inlineConfig: InlineConfig,
/** 当前运行命令:影响插件的 apply('build'|'serve')、默认值选择,以及使用 server/build/preview 哪一支配置 */
command: 'build' | 'serve', //
/** 默认 mode:当没有显式传入 config.mode/--mode 时使用。通常 serve=development,build=production */
defaultMode = 'development',
/** 默认的 process.env.NODE_ENV:没显式设置时给它一个默认值(serve 通常是 development;build 通常是 production) */
defaultNodeEnv = 'development',
/** 是否处于 preview 流程:vite preview 调用解析时会置 true,用于走 config.preview 分支而不是 config.server */
isPreview = false,
/** @internal 内部钩子:解析出 ResolvedConfig 后,允许最后一次"就地修改"配置 */
patchConfig: ((config: ResolvedConfig) => void) | undefined = undefined,
/** @internal 内部钩子:插件数组解析完成后,允许调整/插入/重排插件 */
patchPlugins: ((resolvedPlugins: Plugin[]) => void) | undefined = undefined,
): Promise<ResolvedConfig> { /* ... */ }

上面这个图是这个函数的大概流程,其中忽略了一些逻辑,比如日志打印,声明变量等。主要功能就是处理配置信息,其中需要注意的是对插件的处理,在 vite
中,插件也是可以修改配置信息的,所以需要判断并执行对应的插件。
最终得到的数据如下所示:
yaml
const resolvedConfig = {
// ------ 顶层基础 ------ //
configFile: "/.../vite.config.ts", // 实际使用的配置文件绝对路径
configFileDependencies: ["/.../vite.config.ts"], // 配置文件的依赖(变更时触发重载)
inlineConfig: { server: {} }, // 通过 Node API/CLI 合并进来的"内联配置"
root: "/abs/path/to/project", // 规范化后的项目根目录
base: "/", // 公共基础路径(注入到 import/HTML 资源前缀)
decodedBase: "/", // base 的解码版(处理过 % 编码)
rawBase: "/", // 未加工前的 base,用于分支判断
publicDir: "/abs/.../public", // 原样拷贝到产物的静态资源目录(false/"" 表示禁用)
cacheDir: "/abs/.../node_modules/.vite", // 缓存目录(依赖预构建、元数据缓存等)
command: "serve", // 当前命令:'serve' | 'build'
mode: "development", // 模式:影响 .env 选择与 define 替换
isWorker: false, // 本配置是否用于 worker 环境
mainConfig: null, // 多配置串联时的根配置(默认 null)
bundleChain: [], // 内部构建链信息(调试/实验特性)
isProduction: false, // 基于 NODE_ENV 推断的生产/开发布尔值
// ------ 插件 ------ //
plugins: [/* 内置+用户插件,已按 enforce 段位与顺序排好 */],
// ------ 资源与工具链选项 ------ //
css: {
transformer: "postcss", // CSS 处理器:'postcss'
preprocessorMaxWorkers: true, // 预处理器并行工作线程
devSourcemap: false, // 开发时是否给 CSS 产出 sourcemap
},
json: {
namedExports: true, // JSON 是否导出具名成员
stringify: "auto", // 何时把 JSON 作为字符串处理
},
esbuild: { // esbuild 相关(false 表示禁用某些环节)
jsxDev: true, // 开发 JSX 转换(影响开发体验/警告)
},
// ------ 预览服务器(vite preview)默认 ------ //
preview: {
port: 4173,
strictPort: false, // 端口被占用是否报错
allowedHosts: [],
open: false,
cors: { origin: {} },
headers: {},
},
// ------ 环境变量 ------ //
envDir: "/abs/path/to/project", // 加载 .env 的目录
env: {
BASE_URL: "/", // 注入到客户端可读:import.meta.env.BASE_URL
MODE: "development", // import.meta.env.MODE
DEV: true, // import.meta.env.DEV
PROD: false, // import.meta.env.PROD
},
// ------ 日志与包缓存 ------ //
logger: { hasWarned: false }, // 简化的 logger 状态
packageCache: {}, // 包信息缓存(加速/去重)
// ------ Web Worker 默认 ------ //
worker: {
format: "iife", // worker 打包格式
rollupOptions: {}, // 透传到 rollup 的自定义配置
},
appType: "spa", // 应用类型:'spa' | 'mpa' | 'custom'
experimental: { // 实验性开关(不稳定)
importGlobRestoreExtension: false, // import.meta.glob 恢复扩展名行为
hmrPartialAccept: true, // HMR 部分接受(更细粒度热更)
},
// ------ SSR 顶层(为生态兼容回灌自 environments.ssr) ------ //
ssr: {
target: "node", // SSR 运行目标:'node' | 'webworker'
optimizeDeps: { // SSR 下依赖预构建策略
esbuildOptions: { preserveSymlinks: false },
include: [], exclude: [], needsInterop: [],
extensions: [], holdUntilCrawlEnd: true, force: false, noDiscovery: true,
},
external: [], // 强制 external 的依赖名单
noExternal: [], // 强制内联(不 external)的依赖名单
resolve: {
conditions: ["module", "node", "development|production"], // SSR 解析条件
externalConditions: ["node", "module-sync"], // SSR 外部条件
},
},
// ------ 依赖预构建(客户端为主;与 environments.client.optimizeDeps 共用同一引用) ------ //
optimizeDeps: {
include: [], exclude: [], needsInterop: [],
extensions: [], holdUntilCrawlEnd: true,
force: false, // 强制重新预构建
noDiscovery: false, // 关闭自动扫描(完全手动 include)
esbuildOptions: { preserveSymlinks: false },
},
// ------ 开发服务器(vite dev)运行时选项 ------ //
server: {
port: 5173, strictPort: false, // 端口与占用处理
allowedHosts: [], // 允许的 Host 白名单
open: false, // 启动是否自动打开浏览器
cors: { origin: {} }, // CORS 配置
headers: {}, // 自定义响应头
warmup: { clientFiles: [], ssrFiles: [] }, // 预热文件列表
middlewareMode: false, // 中间件模式(与自定义服务器整合)
fs: {
strict: true, // 严格文件系统访问(限制越界)
deny: [".env", ".env.*", "*.{crt,pem}", "**/.git/**"], // 拒绝访问规则
allow: ["/abs/path/to/project/vite"], // 允许访问白名单
},
preTransformRequests: true, // 预变换请求(加速 dev 首屏)
perEnvironmentStartEndDuringDev: false, // 多环境分阶段启动(实验)
},
// ------ 模块解析策略(顶层;源自 client 环境并已解析) ------ //
resolve: {
externalConditions: ["node", "module-sync"], // 参与外部依赖解析的条件
extensions: [".mjs",".js",".mts",".ts",".jsx",".tsx",".json"], // 解析扩展
dedupe: [], // 强制去重的依赖(共享单实例)
noExternal: [], external: [], // 构建/SSR external 策略
preserveSymlinks: false, // 是否保留符号链接
alias: [
{ find: /*特征*/, replacement: "/@fs/.../env.mjs" }, // 路径别名
{ find: /*特征*/, replacement: "/@fs/.../client.mjs" },
],
mainFields: ["browser","module","jsnext:main","jsnext"], // 优先入口字段
conditions: ["module","browser","development|production"], // 条件导出
builtins: [], // 内建模块别名(供 polyfill 等)
},
// ------ 多环境解析结果(client / ssr ...) ------ //
environments: {
client: {
resolve: { /* 与顶层 resolve 类似,但针对浏览器环境的细化 */ },
keepProcessEnv: false, // 是否保留 process.env 访问(客户端默认 false)
consumer: "client", // 消费者标识:客户端
optimizeDeps: { /* 客户端依赖预构建设置 */ },
dev: {
warmup: [], // dev 预热文件
sourcemap: { js: true }, // js sourcemap
preTransformRequests: true, // 预变换
recoverable: true, // dev 期间可恢复错误
moduleRunnerTransform: false, // Runner 级别变换
},
build: {
target: ["chrome107","edge107","firefox104","safari16"], // 产物 JS/CSS 目标
polyfillModulePreload: true, // 是否注入 modulepreload polyfill
modulePreload: { polyfill: true }, // 同上细化
outDir: "dist", assetsDir: "assets",
sourcemap: false,
terserOptions: {}, rollupOptions: {}, // 透传给 Rollup/Terser
commonjsOptions: { include: [{}], extensions: [".js",".cjs"] },
dynamicImportVarsOptions: { warnOnError: true, exclude: [{}] },
write: true, emptyOutDir: null, copyPublicDir: true,
manifest: false, lib: false, ssrManifest: false, ssrEmitAssets: false,
reportCompressedSize: true, chunkSizeWarningLimit: 500, watch: null,
cssCodeSplit: true, minify: "esbuild",
ssr: false, emitAssets: true, // 客户端构建会产出静态资源
cssTarget: ["chrome107","edge107","firefox104","safari16"],
cssMinify: true,
},
plugins: [/* 客户端生效的插件清单(已排序) */],
},
ssr: {
resolve: { /* 针对 Node/WebWorker SSR 的解析条件/别名等 */ },
keepProcessEnv: true, // SSR 允许直接使用 process.env
consumer: "server",
optimizeDeps: { noDiscovery: true, ... },// SSR 下通常不自动扫描
dev: {
warmup: [], sourcemap: { js: true },
preTransformRequests: false, // SSR dev 不预变换请求
recoverable: false, // 更严格
moduleRunnerTransform: true, // 运行器级变换
},
build: {
// 与 client.build 相似,但:
minify: false, // SSR 一般不压缩,便于调试
ssr: true, emitAssets: false, // 不产出静态资源(由 client 负责)
cssMinify: "esbuild",
},
plugins: [/* SSR 生效的插件清单(已排序) */],
},
},
// ------ 运行期工具 ------ //
webSocketToken: "oTQiPWuOywdb", // HMR/WS 鉴别 token(随机 72bits)
safeModulePaths: new Set(), // 标记为"安全可加载"的模块路径集合
}
具体的配置处理逻辑由于篇幅限制就不一行一行的说明了,有兴趣可看一下 vite
源码中的 resolveConfig
这个函数即可。