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

前言

总结一下看vite的一些收获,有误记得指出哈哈

cli的入口

文件位于vite/bin/vite.js

  • 主要功能是在vitejs的开发环境下开启source-map-support增强错误信息的可读性
// 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信息
  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信息
  if (filterIndex > 0) {
    // 过滤模块的debug信息
    const filter = process.argv[filterIndex + 1]
    if (filter && !filter.startsWith('-')) {
      process.env.VITE_DEBUG_FILTER = filter
    }
  }
  • profile 参数启动nodejs分析并将Session实例挂载到全局环境中
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

  //  指定配置文件
  .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
  .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`,
  )
  • 创建服务实例与启动
 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()
  • 打印启动时间与服务地址
    // 日志函数
      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 结束分析或者开启分析
  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
  // 设置最终构建的浏览器兼容目标
  .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打包配置
// 过滤重复的选项
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
  .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)`)
  • 启动预览服务
 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启动

// 生成帮助信息 -h --help
cli.help()
// 生成版本信息 -v --version
cli.version(VERSION)
// 解析命令行参数
cli.parse()

最后

vite究极青春版:github.com/baicie/vite…
vite frok注释:github.com/baicie/vite

原文链接:https://juejin.cn/post/7337125978803208244 作者:白cie

(0)
上一篇 2024年2月20日 上午10:05
下一篇 2024年2月20日 上午10:15

相关推荐

发表回复

登录后才能评论