本文是Vite 原理揭秘 的第二篇,该系列的其它文章如下:
前言
在彻底搞懂 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
,其内容如下。
文件的最后调用了
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
,这里的argument1
和argument2
就是命令行参数。
在 Node.js 中有一个全局的环境变量 process
,该变量上有一个 argv
属性。argv
属性的值是一个数组。数组的第一位是 node 程序可执行文件的绝对路径,数组的第二位是被执行的 js 文件的绝对路径,剩下的元素是命令行中追加的参数。
我们用下面这段 js 来试一下
import { argv } from 'node:process';
// 打印 argv 对象
console.log(argv)
命令行中输入 node index.js argument1 argument2
,得到的结果如下。
可以看到数组的前两位分别是 Node.js 可执行程序的绝对路径和被执行的 js 文件的绝对路径,剩下的是我们在命令行追加的参数。
认识 cac 包
cac 可以帮助你解析命令行中的参数,创建命令行界面。类似的包还有 commander ,yargs。
cac 轻量,易用且功能强大。最常用的两个方法是 command
和option
。command
方法可以解析命令行中的命令参数,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 对象。
接着链式调用 option 方法来解析选项参数。--config
--base
等这些选项是不和命令挂钩的,所以直接调用 option 方法解析。
接着调用command
方法解析和命令挂钩的选项。这些命令分别对应 Vite 提供四种命令:
不提供命令
,serve
,dev
启动开发服务器build
打包optimize
预构建preview
预览
我们看和启动开发服务器相关的。
想要启动开发服务器,输入vite
既可,不需要在vite
后面添加任何命令。command
第一个参数中的[root]
代表的是项目根目录参数,同vite.config.js
中的 root
属性。
Vite 还提供了启动开发服务的别名serve
和dev
,所以vite serve
或者vite dev
也可以启动开发服务,所以需要调用 alias 方法把这两个命令也加上去。
最后通过action方法获取到命令行解析的结果。
打开 action 方法,该方法接受一个回调函数,回调函数接收的是命令行解析的结果。可以看到 vite 利用这些解析的结果调用 createServer
方法创建了一个服务。
createServer
方法在 src/node/serve/index.ts
中。该方法是 Vite 最重要的方法。
下一节我们将继续研究该方法。