命令行参数的处理

492 阅读5分钟

本文是Vite 原理揭秘 的第二篇,该系列的其它文章如下:

1.彻底搞懂 npm run 原理

前言

彻底搞懂 npm run 原理这篇文章中我们通过分析npm run command命令的原理,了解到npm run command最后会将你的脚本输入到执行一个 cmd 文件。我们再来看一下在局部安装Vite时生成的 cmd 文件。

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\..\vite\bin\vite.js" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\..\vite\bin\vite.js" %*
)

node  "%~dp0\..\vite\bin\vite.js" %* 的意思是通过 nodejs 执行 js 文件。在这段代码的末尾出现了%*,这两个符号是指在执行 vite.cmd这个文件时,命令行中的参数。等同于在命令行中输入 node ../vite/bin/vite.js 命令参数,所以我们才能在 vite.js 文件中接收到命令行参数。

上一节中我们找到了 Vite 启动的入口文件 bin/vite.js,其内容如下。

Snipaste_2023-10-28_16-16-11.jpg 文件的最后调用了 start方法,start 方法的作用是 import ../dist/node/cli.js 这个文件,被 import 的文件接着将会执行。 这里的 ../dist/node/cli.js 路径我们需要换成 ../src/node/cli.ts,因为我们看的是未打包 src目录下的源码。

什么是命令行参数

当你在命令行中运行一个程序时,你可以提供额外的信息来供被运行的程序使用,这些额外的信息就是命令行参数

比如当你在命令行中输入 node --version 时,你需要运行的程序是 node--version 就是提供给 node 程序的额外信息,也就是命令行参数。--version 参数告诉 node 打印出当前的版本号。

nodejs 脚本中获取命令行参数

当我们想用 nodejs 来运行某个js文件时,只需要在 node 命令后面加上要被执行的 js 文件路径即可。比如 node index.js 就可以执行 index.js 这个 js 文件。在index.js之后我们可以继续在后面追加信息,比如 node index.js --name。这有什么用呢?假如我们想在 js 脚本中区分是 dev模式还是pro模式。那么我们可以这样 node index.js dev,这里的 dev 会被作为命令行参数传给我们的 js 脚本。

那么如果在我们的脚本中获取 nodejs 的命令行参数呢?答案是利用 nodejs 的全局对象 process 上的 argv 属性。该属性值是一个数组。

在启动 Node.js 程序时可以在命令行的后面追加你需要的参数。比如 node example.js argument1 argument2,这里的argument1argument2就是命令行参数。

在 Node.js 中有一个全局的环境变量 process,该变量上有一个 argv 属性。argv属性的值是一个数组。数组的第一位是 node 程序可执行文件的绝对路径,数组的第二位是被执行的 js 文件的绝对路径,剩下的元素是命令行中追加的参数。

我们用下面这段 js 来试一下

import { argv } from 'node:process';

// 打印 argv 对象
console.log(argv)

命令行中输入 node index.js argument1 argument2,得到的结果如下。

Snipaste_2023-10-28_16-02-05.jpg 可以看到数组的前两位分别是 Node.js 可执行程序的绝对路径和被执行的 js 文件的绝对路径,剩下的是我们在命令行追加的参数。

认识 cac 包

cac 可以帮助你解析命令行中的参数,创建命令行界面。类似的包还有 commander yargs

cac 轻量,易用且功能强大。最常用的两个方法是 commandoptioncommand方法可以解析命令行中的命令参数,option 方法可以解析命令行中的选项参数。两者还可以连用,将选项和命令联系起来。

你可以直接使用option方法解析一个选项参数

import { cac } from "cac";
const cli = cac("cli");

cli.option('--type [type]', 'Choose a project type')
const parsed = cli.parse()
// 打印解析的结果
console.log(JSON.stringify(parsed))

也可以将选项解析和命令解析联系,这样 cac 只会解析 test 命令后面的选项参数。

import { cac } from "cac";
const cli = cac("cli");
cli
  .command('test', 'Test command')
  .option('--type [type]', 'Choose a project type')
  .action(options => {
    console.log(options)
  })

cli.parse()

Vite 命令行参数的解析

我们打开../src/node/cli.ts这个文件,首先引入 cac 并创建 cli 对象。

图片.png

接着链式调用 option 方法来解析选项参数。--config --base等这些选项是不和命令挂钩的,所以直接调用 option 方法解析。

图片.png

接着调用command方法解析和命令挂钩的选项。这些命令分别对应 Vite 提供四种命令:

  1. 不提供命令servedev 启动开发服务器
  2. build 打包
  3. optimize 预构建
  4. preview 预览

我们看和启动开发服务器相关的。

想要启动开发服务器,输入vite既可,不需要在vite后面添加任何命令。command第一个参数中的[root]代表的是项目根目录参数,同vite.config.js中的 root属性。

Vite 还提供了启动开发服务的别名servedev,所以vite serve或者vite dev也可以启动开发服务,所以需要调用 alias 方法把这两个命令也加上去。

最后通过action方法获取到命令行解析的结果。 图片.png

打开 action 方法,该方法接受一个回调函数,回调函数接收的是命令行解析的结果。可以看到 vite 利用这些解析的结果调用 createServer 方法创建了一个服务。

图片.png

createServer 方法在 src/node/serve/index.ts中。该方法是 Vite 最重要的方法。 下一节我们将继续研究该方法。