关于vite源码一些自己的理解 (一):cli

前言

总结一下看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 是一个功能丰富、易于使用的命令行解析库,适用于各种类型的命令行应用程序开发

优点:

  1. 简单易用cac 的 API 设计简洁明了,易于理解和使用,使得创建命令行应用程序变得简单快捷。
  2. 支持命令和选项 :你可以使用 cac 来定义命令和选项,并为每个命令和选项编写对应的处理逻辑。它支持单个命令、带有子命令的命令,以及各种类型的选项,如布尔型、字符串型等。
  3. 自动生成帮助信息cac 可以自动生成帮助信息,包括命令列表、选项说明等,使得用户可以方便地了解如何使用你的命令行应用程序。
  4. 插件系统cac 支持插件系统,你可以轻松地为你的 CLI 应用程序添加额外的功能或扩展。
  5. 异步命令处理:命令处理函数可以是异步的,这使得你可以处理异步操作,如文件读写、网络请求等。
  6. 可嵌入性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

相关推荐
崔庆才丨静觅5 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax