前言
总结一下看vite的一些收获,有误记得指出哈哈
cli的入口
文件位于vite/bin/vite.js
- 主要功能是在vitejs的开发环境下开启source-map-support增强错误信息的可读性
ts
// import.meta.url 用于获取当前文件的URL地址 如果不包含node_modules则证明处于vite项目的开发环境
if (!import.meta.url.includes('node_modules')) {
try {
// only available as dev dependency
// source-map-support 能在nodejs环境下增强错误信息的可读性
await import('source-map-support').then((r) => r.default.install())
} catch (e) { }
}
- debug 是否开启debug模式以及开启某几个模块的debug信息
ts
let value = process.argv[debugIndex + 1]
// 如果是--debug打头则打印所有模块的debug信息
if (!value || value.startsWith('-')) {
value = 'vite:*'
} else {
// 否则就拼接多个模块的debug信息
// support debugging multiple flags with comma-separated list
value = value
.split(',')
.map((v) => `vite:${v}`)
.join(',')
}
// 设置环境变量
process.env.DEBUG = `${process.env.DEBUG ? process.env.DEBUG + ',' : ''
}${value}`
- filter 过滤某些模块的debug信息
ts
if (filterIndex > 0) {
// 过滤模块的debug信息
const filter = process.argv[filterIndex + 1]
if (filter && !filter.startsWith('-')) {
process.env.VITE_DEBUG_FILTER = filter
}
}
- profile 参数启动nodejs分析并将Session实例挂载到全局环境中
ts
if (profileIndex > 0) {
process.argv.splice(profileIndex, 1)
const next = process.argv[profileIndex]
if (next && !next.startsWith('-')) {
// 删除profile 参数
process.argv.splice(profileIndex, 1)
}
// 启动nodejs分析
const inspector = await import('node:inspector').then((r) => r.default)
// 创建一个session 用于连接到nodejs的调试器 并设置到全局变量中
const session = (global.__vite_profile_session = new inspector.Session())
session.connect()
session.post('Profiler.enable', () => {
session.post('Profiler.start', start)
})
} else {
// 原神启动
start()
}
cac
简介:cac
是一个功能丰富、易于使用的命令行解析库,适用于各种类型的命令行应用程序开发
优点:
- 简单易用 :
cac
的 API 设计简洁明了,易于理解和使用,使得创建命令行应用程序变得简单快捷。 - 支持命令和选项 :你可以使用
cac
来定义命令和选项,并为每个命令和选项编写对应的处理逻辑。它支持单个命令、带有子命令的命令,以及各种类型的选项,如布尔型、字符串型等。 - 自动生成帮助信息 :
cac
可以自动生成帮助信息,包括命令列表、选项说明等,使得用户可以方便地了解如何使用你的命令行应用程序。 - 插件系统 :
cac
支持插件系统,你可以轻松地为你的 CLI 应用程序添加额外的功能或扩展。 - 异步命令处理:命令处理函数可以是异步的,这使得你可以处理异步操作,如文件读写、网络请求等。
- 可嵌入性 :
cac
可以与其他 Node.js 应用程序轻松集成,作为一个模块来使用,从而为应用程序添加命令行界面。
common options
vite命令中通用的option
ts
// 指定配置文件
.option('-c, --config <file>', `[string] use specified config file`)
// 指定根路径
.option('--base <path>', `[string] public base path (default: /)`, {
type: [convertBase],
})
// 指定日志级别
.option('-l, --logLevel <level>', `[string] info | warn | error | silent`)
// 指定是否清空屏幕
.option('--clearScreen', `[boolean] allow/disable clear screen when logging`)
// 是否开启debug模式或者开启某些模块的debug模式
.option('-d, --debug [feat]', `[string | boolean] show debug logs`)
// 过滤debug日志
.option('-f, --filter <filter>', `[string] filter debug logs`)
// 指定环境模式
.option('-m, --mode <mode>', `[string] set env mode`)
dev
文件位于vite/src/node/cli.ts
- dev options
ts
.command('[root]', 'start dev server') // default command
// 别名 vite serve
.alias('serve') // the command is called 'serve' in Vite's API
// 别名 vite dev
.alias('dev') // alias to align with the script name
// 指定host
.option('--host [host]', `[string] specify hostname`, { type: [convertHost] })
// 指定端口
.option('--port <port>', `[number] specify port`)
// 是否打开浏览器
.option('--open [path]', `[boolean | string] open browser on startup`)
// 是否开启CORS
.option('--cors', `[boolean] enable CORS`)
// 锁死端口 如果端口被占用则退出
.option('--strictPort', `[boolean] exit if specified port is already in use`)
// 忽略之前的预构建与缓存
.option(
'--force',
`[boolean] force the optimizer to ignore the cache and re-bundle`,
)
- 创建服务实例与启动
ts
const server = await createServer({
root,
base: options.base,
mode: options.mode,
configFile: options.config,
logLevel: options.logLevel,
clearScreen: options.clearScreen,
optimizeDeps: { force: options.force },
server: cleanOptions(options),
})
if (!server.httpServer) {
throw new Error('HTTP server not available')
}
// 启动服务
await server.listen()
- 打印启动时间与服务地址
ts
// 日志函数
const info = server.config.logger.info
// 是否打印时间
const viteStartTime = global.__vite_start_time ?? false
// 拼接启动时间
const startupDurationString = viteStartTime
? colors.dim(
`ready in ${colors.reset(
colors.bold(Math.ceil(performance.now() - viteStartTime)),
)} ms`,
)
: ''
// 是否有现有日志
const hasExistingLogs =
process.stdout.bytesWritten > 0 || process.stderr.bytesWritten > 0
// 打印启动信息
info(
`\n ${colors.green(
`${colors.bold('VITE')} v${VERSION}`,
)} ${startupDurationString}\n`,
{
clear: !hasExistingLogs,
},
)
// 打印服务地址
server.printUrls()
- 自定义快捷键 p+enter 结束分析或者开启分析
ts
const customShortcuts: CLIShortcut<typeof server>[] = []
if (profileSession) {
customShortcuts.push({
// 键盘 p
key: 'p',
description: 'start/stop the profiler',
// 回掉函数
async action(server) {
if (profileSession) {
// 如果有分析实例 停止分析 输出分析结果
await stopProfiler(server.config.logger.info)
} else {
// 开启分析
const inspector = await import('node:inspector').then(
(r) => r.default,
)
await new Promise<void>((res) => {
profileSession = new inspector.Session()
profileSession.connect()
profileSession.post('Profiler.enable', () => {
profileSession!.post('Profiler.start', () => {
server.config.logger.info('Profiler started')
res()
})
})
})
}
},
})
}
// 绑定自定义快捷键
server.bindCLIShortcuts({ print: true, customShortcuts })
build
- build options
ts
// 设置最终构建的浏览器兼容目标
.option('--target <target>', `[string] transpile target (default: 'modules')`)
// 设置输出目录
.option('--outDir <dir>', `[string] output directory (default: dist)`)
// 指定生成静态资源的存放路径
.option(
'--assetsDir <dir>',
`[string] directory under outDir to place assets in (default: assets)`,
)
// 小于此阈值的导入或引用资源将内联为 base64 编码,以避免额外的 http 请求
.option(
'--assetsInlineLimit <number>',
`[number] static asset base64 inline threshold in bytes (default: 4096)`,
)
// 生成面向 SSR 的构建
.option(
'--ssr [entry]',
`[string] build specified entry for server-side rendering`,
)
// 构建后是否生成 source map 文件
.option(
'--sourcemap [output]',
`[boolean | "inline" | "hidden"] output source maps for build (default: false)`,
)
// 设置为 false 可以禁用最小化混淆,或是用来指定使用哪种混淆器
.option(
'--minify [minifier]',
`[boolean | "terser" | "esbuild"] enable/disable minification, ` +
`or specify minifier to use (default: esbuild)`,
)
// manifest 文件的名字 包含了没有被 hash 过的资源文件名和 hash 后版本的映射
.option('--manifest [name]', `[boolean | string] emit build manifest json`)
// 构建也将生成 SSR 的 manifest 文件
.option('--ssrManifest [name]', `[boolean | string] emit ssr manifest json`)
// 构建时清空该目录
.option(
'--emptyOutDir',
`[boolean] force empty outDir when it's outside of root`,
)
// 是否启用 rollup 的监听器
.option('-w, --watch', `[boolean] rebuilds when modules have changed on disk`)
- 调用内建rollup打包配置
ts
// 过滤重复的选项
filterDuplicateOptions(options)
const { build } = await import('./build')
// 清理选项
const buildOptions: BuildOptions = cleanOptions(options)
try {
// 构建
await build({
...
} finally {
// 分析
stopProfiler((message) => createLogger(options.logLevel).info(message))
}
optimize
- optimize 扫描并优化项目内的依赖关系
- option force 忽略之前已经缓存过的、已经优化过的依赖
preview
- preview option
ts
.command('preview [root]', 'locally preview production build')
// 指定host
.option('--host [host]', `[string] specify hostname`, { type: [convertHost] })
// 指定端口
.option('--port <port>', `[number] specify port`)
// 锁死端口 如果端口被占用则退出
.option('--strictPort', `[boolean] exit if specified port is already in use`)
// 是否打开浏览器
.option('--open [path]', `[boolean | string] open browser on startup`)
// 指定输出目录
.option('--outDir <dir>', `[string] output directory (default: dist)`)
- 启动预览服务
ts
const { preview } = await import('./preview')
try {
// 启动预览服务
const server = await preview({
//...
})
// 打印服务地址
server.printUrls()
// 绑定快捷键(默认又俩 h+enter查看)
server.bindCLIShortcuts({ print: true })
} catch (e) {
createLogger(options.logLevel).error(
colors.red(`error when starting preview server:\n${e.stack}`),
{ error: e },
)
process.exit(1)
} finally {
// 停止nodejs分析
stopProfiler((message) => createLogger(options.logLevel).info(message))
}
xx启动
ts
// 生成帮助信息 -h --help
cli.help()
// 生成版本信息 -v --version
cli.version(VERSION)
// 解析命令行参数
cli.parse()
最后
vite究极青春版:github.com/baicie/vite...
vite frok注释:github.com/baicie/vite