//server 只在开发环境使用,默认只需关注development
const config = await resolveConfig(inlineConfig, 'serve', 'development')
源码位置:packages\vite\src\node\config.ts
核心代码如下:
export async function resolveConfig(
inlineConfig: InlineConfig,
command: 'build' | 'serve',
defaultMode = 'development'
): Promise<ResolvedConfig> {
let config = inlineConfig
let configFileDependencies: string[] = []
let mode = inlineConfig.mode || defaultMode
let { configFile } = config
//默认情况下,执行 vite dev,configFile 为 undefined
//执行 vite dev --config vite.congfig.ts 时, configFile为vite.congfig.ts
// configFile!==false 总是为true
if (configFile !== false) {
//加载本地的配置文件,可以是命令行或自动从根目录查找
const loadResult = await loadConfigFromFile(
configEnv,
configFile,
config.root,
config.logLevel
)
if (loadResult) {
config = mergeConfig(loadResult.config, config)
configFile = loadResult.path
configFileDependencies = loadResult.dependencies
}
}
// user config may provide an alternative mode. But --mode has a higher priority
mode = inlineConfig.mode || config.mode || mode
configEnv.mode = mode
// resolve plugins
// 解析配置文件里面的插件
const rawUserPlugins = (config.plugins || []).flat(Infinity).filter((p) => {
if (!p) {
return false
} else if (!p.apply) {
return true
} else if (typeof p.apply === 'function') {
return p.apply({ ...config, mode }, configEnv)
} else {
return p.apply === command
}
}) as Plugin[]
const [prePlugins, normalPlugins, postPlugins] =
sortUserPlugins(rawUserPlugins)
// resolve worker
const resolvedWorkerOptions: ResolveWorkerOptions = {
format: config.worker?.format || 'iife',
plugins: [],
rollupOptions: config.worker?.rollupOptions || {}
}
// run config hooks
const userPlugins = [...prePlugins, ...normalPlugins, ...postPlugins]
for (const p of userPlugins) {
if (p.config) {
const res = await p.config(config, configEnv)
if (res) {
config = mergeConfig(config, res)
}
}
}
// resolve root
const resolvedRoot = normalizePath(
config.root ? path.resolve(config.root) : process.cwd()
)
const clientAlias = [
{ find: /^[\/]?@vite\/env/, replacement: () => ENV_ENTRY },
{ find: /^[\/]?@vite\/client/, replacement: () => CLIENT_ENTRY }
]
// resolve alias with internal client alias
const resolvedAlias = normalizeAlias(
mergeAlias(
// @ts-ignore because @rollup/plugin-alias' type doesn't allow function
// replacement, but its implementation does work with function values.
clientAlias,
config.resolve?.alias || config.alias || []
)
)
const resolveOptions: ResolvedConfig['resolve'] = {
dedupe: config.dedupe,
...config.resolve,
alias: resolvedAlias
}
// load .env files
const envDir = config.envDir
? normalizePath(path.resolve(resolvedRoot, config.envDir))
: resolvedRoot
const userEnv =
inlineConfig.envFile !== false &&
loadEnv(mode, envDir, resolveEnvPrefix(config))
// Note it is possible for user to have a custom mode, e.g. `staging` where
// production-like behavior is expected. This is indicated by NODE_ENV=production
// loaded from `.staging.env` and set by us as VITE_USER_NODE_ENV
const isProduction = (process.env.VITE_USER_NODE_ENV || mode) === 'production'
if (isProduction) {
// in case default mode was not production and is overwritten
process.env.NODE_ENV = 'production'
}
// resolve public base url
const BASE_URL = resolveBaseUrl(config.base, command === 'build', logger)
const resolvedBuildOptions = resolveBuildOptions(config.build)
// resolve cache directory
const pkgPath = lookupFile(resolvedRoot, [`package.json`], { pathOnly: true })
const cacheDir = config.cacheDir
? path.resolve(resolvedRoot, config.cacheDir)
: pkgPath
? path.join(path.dirname(pkgPath), `node_modules/.vite`)
: path.join(resolvedRoot, `.vite`)
const assetsFilter = config.assetsInclude
? createFilter(config.assetsInclude)
: () => false
const { publicDir } = config
const resolvedPublicDir =
publicDir !== false && publicDir !== ''
? path.resolve(
resolvedRoot,
typeof publicDir === 'string' ? publicDir : 'public'
)
: ''
const server = resolveServerOptions(resolvedRoot, config.server, logger)
const optimizeDeps = config.optimizeDeps || {}
const resolved: ResolvedConfig = {
...config,
configFile: configFile ? normalizePath(configFile) : undefined,
configFileDependencies: configFileDependencies.map((name) =>
normalizePath(path.resolve(name))
),
inlineConfig,
root: resolvedRoot,
base: BASE_URL,
resolve: resolveOptions,
publicDir: resolvedPublicDir,
cacheDir,
command,
mode,
isWorker: false,
isProduction,
plugins: userPlugins,
server,
build: resolvedBuildOptions,
preview: resolvePreviewOptions(config.preview, server),
env: {
...userEnv,
BASE_URL,
MODE: mode,
DEV: !isProduction,
PROD: isProduction
},
assetsInclude(file: string) {
return DEFAULT_ASSETS_RE.test(file) || assetsFilter(file)
},
logger,
packageCache: new Map(),
createResolver,
optimizeDeps: {
...optimizeDeps,
esbuildOptions: {
keepNames: optimizeDeps.keepNames,
preserveSymlinks: config.resolve?.preserveSymlinks,
...optimizeDeps.esbuildOptions
}
},
worker: resolvedWorkerOptions
}
// flat config.worker.plugin
const [workerPrePlugins, workerNormalPlugins, workerPostPlugins] =
sortUserPlugins(config.worker?.plugins as Plugin[])
const workerResolved: ResolvedConfig = { ...resolved, isWorker: true }
resolved.worker.plugins = await resolvePlugins(
workerResolved,
workerPrePlugins,
workerNormalPlugins,
workerPostPlugins
)
// call configResolved worker plugins hooks
await Promise.all(
resolved.worker.plugins.map((p) => p.configResolved?.(workerResolved))
)
//resolvePlugins 位于 ./plugins 目录,下一章节分析
;(resolved.plugins as Plugin[]) = await resolvePlugins(
resolved,
prePlugins,
normalPlugins,
postPlugins
)
return resolved
}
最终解析出的 resolved 大概如下:
{
base:'/',
// 项目的根目录, 一般是项目目录
root:'……',
// 构建缓存的文件夹, 默认情况下是项目目录中 node_modules 下的.vite文件
cacheDir:'……/node_modules/.vite',
// 命令模式是 serve 还是 build
command:'serve',
// 环境 development、 production,与 process.env.NODE_ENV 一致
mode:'development',
// vite 配置文件位置
configFile:'……/vite.config.js',
// server 需要运行的 public 文件夹
publicDir:'……/public',
configFileDependencies:['vite.config.js'],
// 这个就是用户自定义使用 vue 插件设置的配置属性,在 vue 插件中被使用
define:{
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
},
env:{
BASE_URL: '/',
MODE: 'development',
DEV: true,
PROD: false
},
esbuild:{include: /\\.ts$/},
// createServer 中传入命令行参数, 都为 undefined
inlineConfig:{root: undefined, base: undefined, mode: undefined, configFile: undefined, logLevel: undefined, …},
isProduction: false,
optimizeDeps:{esbuildOptions: {…}}
// 插件集合,结果为上面介绍的插件列表
plugins:[],
……
}
<pre data-line="0"><code class="language-typescript" language=typescript><span class="code-block">//server 只在开发环境使用,默认只需关注development
const config = await resolveConfig(inlineConfig, 'serve', 'development')</span></code></pre>
<p data-line="4">源码位置:<code>packages\vite\src\node\config.ts</code></p>
<p data-line="6">核心代码如下:</p>
<pre data-line="7"><code class="language-typescript" language=typescript><span class="code-block">export async function resolveConfig(
inlineConfig: InlineConfig,
command: 'build' | 'serve',
defaultMode = 'development'
): Promise<ResolvedConfig> {
let config = inlineConfig
let configFileDependencies: string[] = []
let mode = inlineConfig.mode || defaultMode
let { configFile } = config
//默认情况下,执行 vite dev,configFile 为 undefined
//执行 vite dev --config vite.congfig.ts 时, configFile为vite.congfig.ts
// configFile!==false 总是为true
if (configFile !== false) {
//加载本地的配置文件,可以是命令行或自动从根目录查找
const loadResult = await loadConfigFromFile(
configEnv,
configFile,
config.root,
config.logLevel
)
if (loadResult) {
config = mergeConfig(loadResult.config, config)
configFile = loadResult.path
configFileDependencies = loadResult.dependencies
}
}
// user config may provide an alternative mode. But --mode has a higher priority
mode = inlineConfig.mode || config.mode || mode
configEnv.mode = mode
// resolve plugins
// 解析配置文件里面的插件
const rawUserPlugins = (config.plugins || []).flat(Infinity).filter((p) => {
if (!p) {
return false
} else if (!p.apply) {
return true
} else if (typeof p.apply === 'function') {
return p.apply({ ...config, mode }, configEnv)
} else {
return p.apply === command
}
}) as Plugin[]
const [prePlugins, normalPlugins, postPlugins] =
sortUserPlugins(rawUserPlugins)
// resolve worker
const resolvedWorkerOptions: ResolveWorkerOptions = {
format: config.worker?.format || 'iife',
plugins: [],
rollupOptions: config.worker?.rollupOptions || {}
}
// run config hooks
const userPlugins = [...prePlugins, ...normalPlugins, ...postPlugins]
for (const p of userPlugins) {
if (p.config) {
const res = await p.config(config, configEnv)
if (res) {
config = mergeConfig(config, res)
}
}
}
// resolve root
const resolvedRoot = normalizePath(
config.root ? path.resolve(config.root) : process.cwd()
)
const clientAlias = [
{ find: /^[\/]?@vite\/env/, replacement: () => ENV_ENTRY },
{ find: /^[\/]?@vite\/client/, replacement: () => CLIENT_ENTRY }
]
// resolve alias with internal client alias
const resolvedAlias = normalizeAlias(
mergeAlias(
// @ts-ignore because @rollup/plugin-alias' type doesn't allow function
// replacement, but its implementation does work with function values.
clientAlias,
config.resolve?.alias || config.alias || []
)
)
const resolveOptions: ResolvedConfig['resolve'] = {
dedupe: config.dedupe,
...config.resolve,
alias: resolvedAlias
}
// load .env files
const envDir = config.envDir
? normalizePath(path.resolve(resolvedRoot, config.envDir))
: resolvedRoot
const userEnv =
inlineConfig.envFile !== false &&
loadEnv(mode, envDir, resolveEnvPrefix(config))
// Note it is possible for user to have a custom mode, e.g. `staging` where
// production-like behavior is expected. This is indicated by NODE_ENV=production
// loaded from `.staging.env` and set by us as VITE_USER_NODE_ENV
const isProduction = (process.env.VITE_USER_NODE_ENV || mode) === 'production'
if (isProduction) {
// in case default mode was not production and is overwritten
process.env.NODE_ENV = 'production'
}
// resolve public base url
const BASE_URL = resolveBaseUrl(config.base, command === 'build', logger)
const resolvedBuildOptions = resolveBuildOptions(config.build)
// resolve cache directory
const pkgPath = lookupFile(resolvedRoot, [`package.json`], { pathOnly: true })
const cacheDir = config.cacheDir
? path.resolve(resolvedRoot, config.cacheDir)
: pkgPath
? path.join(path.dirname(pkgPath), `node_modules/.vite`)
: path.join(resolvedRoot, `.vite`)
const assetsFilter = config.assetsInclude
? createFilter(config.assetsInclude)
: () => false
const { publicDir } = config
const resolvedPublicDir =
publicDir !== false && publicDir !== ''
? path.resolve(
resolvedRoot,
typeof publicDir === 'string' ? publicDir : 'public'
)
: ''
const server = resolveServerOptions(resolvedRoot, config.server, logger)
const optimizeDeps = config.optimizeDeps || {}
const resolved: ResolvedConfig = {
...config,
configFile: configFile ? normalizePath(configFile) : undefined,
configFileDependencies: configFileDependencies.map((name) =>
normalizePath(path.resolve(name))
),
inlineConfig,
root: resolvedRoot,
base: BASE_URL,
resolve: resolveOptions,
publicDir: resolvedPublicDir,
cacheDir,
command,
mode,
isWorker: false,
isProduction,
plugins: userPlugins,
server,
build: resolvedBuildOptions,
preview: resolvePreviewOptions(config.preview, server),
env: {
...userEnv,
BASE_URL,
MODE: mode,
DEV: !isProduction,
PROD: isProduction
},
assetsInclude(file: string) {
return DEFAULT_ASSETS_RE.test(file) || assetsFilter(file)
},
logger,
packageCache: new Map(),
createResolver,
optimizeDeps: {
...optimizeDeps,
esbuildOptions: {
keepNames: optimizeDeps.keepNames,
preserveSymlinks: config.resolve?.preserveSymlinks,
...optimizeDeps.esbuildOptions
}
},
worker: resolvedWorkerOptions
}
// flat config.worker.plugin
const [workerPrePlugins, workerNormalPlugins, workerPostPlugins] =
sortUserPlugins(config.worker?.plugins as Plugin[])
const workerResolved: ResolvedConfig = { ...resolved, isWorker: true }
resolved.worker.plugins = await resolvePlugins(
workerResolved,
workerPrePlugins,
workerNormalPlugins,
workerPostPlugins
)
// call configResolved worker plugins hooks
await Promise.all(
resolved.worker.plugins.map((p) => p.configResolved?.(workerResolved))
)
//resolvePlugins 位于 ./plugins 目录,下一章节分析
;(resolved.plugins as Plugin[]) = await resolvePlugins(
resolved,
prePlugins,
normalPlugins,
postPlugins
)
return resolved
}</span></code></pre>
<p data-line="222">最终解析出的 resolved 大概如下:</p>
<pre data-line="223"><code class="language-typescript" language=typescript><span class="code-block">{
base:'/',
// 项目的根目录, 一般是项目目录
root:'……',
// 构建缓存的文件夹, 默认情况下是项目目录中 node_modules 下的.vite文件
cacheDir:'……/node_modules/.vite',
// 命令模式是 serve 还是 build
command:'serve',
// 环境 development、 production,与 process.env.NODE_ENV 一致
mode:'development',
// vite 配置文件位置
configFile:'……/vite.config.js',
// server 需要运行的 public 文件夹
publicDir:'……/public',
configFileDependencies:['vite.config.js'],
// 这个就是用户自定义使用 vue 插件设置的配置属性,在 vue 插件中被使用
define:{
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
},
env:{
BASE_URL: '/',
MODE: 'development',
DEV: true,
PROD: false
},
esbuild:{include: /\\.ts$/},
// createServer 中传入命令行参数, 都为 undefined
inlineConfig:{root: undefined, base: undefined, mode: undefined, configFile: undefined, logLevel: undefined, …},
isProduction: false,
optimizeDeps:{esbuildOptions: {…}}
// 插件集合,结果为上面介绍的插件列表
plugins:[],
……
}</span></code></pre>