likes
comments
collection
share

彻底搞懂 npm run 原理前言 这是 Vite 源码系列的开篇,在该系列中我们会根据 Vite 流程来阅读它的源码。

作者站长头像
站长
· 阅读数 23

前言

在分析 Vite 原理之前,我们需要先了解一下 npm 包的全局安装、局部安装以及 npm run <command> 这个命令的原理。有了这些前置条件,我们才能找到 vite 的入口文件,从而开启 vite 世界大门,对它的实现细节一探究竟。

<command>表示这是一个可选参数。如果提供了 commandnpm run 会执行package.json 文件中 scripts 对象上和 command 同名的脚本,否则会列出 package.json 文件中 scripts 对象上所有的脚本。

在分析完npm run command 原理后,我们会写一个 nodejs 脚本模拟这个命令执行的过程,来加深对其原理的理解。

最后我们在上面的前置条件的基础上,一起寻找 Vite 的入口文件,为分析 Vite 做准备。

全局安装

当使用 npm install -global [package] 安装包时,包会被安装在 nodejs 安装目录(安装 nodejs 时选择的安装路径)的 node_modules 文件夹下,同时会在 nodejs 安装目录创建和包同名的可执行文件。这些同名的可执行文件被执行时会通过 nodejs 来执行 node_modules 目录下对应包中的 js 文件。

由于 nodejs 安装目录在安装 nodejs 时已经被加入到 Path 环境变量中,所以在命令行中输入和可执行文件同名的命令就可以直接调用这些可执行文件。

看个例子,下面是在全局安装 Vite 时生成的 cmd 可执行文件内容:

@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\node_modules\vite\bin\vite.js" %*

可以看到该可执行文件的最后一行逻辑是通过 nodejs 来执行 ./node_modules/vite/bin/vite.js 这个 js 文件。

使用 nvm 管理 nodejs 时全局安装目录会有所变化。可通过 npm root -g 命令查看全局包的具体安装位置。

局部安装

安装包时如果不添加 -g 或者 -global 选项,包会被安装在当前目录下的 node_modules/ 文件夹中。 如果被安装的包package.json中有 bin 这个属性,npm 就会在当前目录下的 node_modules/.bin 文件夹中创建同名的可执行文件。这些同名的可执行文件和全局安装时生成的可执行文件作用相同,被执行时会通过 nodejs 来执行 当前目录下的node_modules 目录下的对应的包中的 js 文件(通常这个 js 文件放在包的 .bin文件夹中)。

局部安装时当前目录下的 node_modules/.bin路径没有被添加到 Path 环境变量中,所以无法在命令行直接输入同名的命令来执行。

但是 npm 给我们提供了一种简单的方式,就是 npm run command 这个命令。首先在 package.json 中的上 scripts 对象上添加一个属性,属性名对应 npm run command 命令中的 command属性值是你需要执行的具体命令。比如属性名是 dev,属性值是 vite dev。然后在当前目录下命令行中输入 npm run dev 就可以调用 Vite 了。

看个例子,下面是在局部安装 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" %*
)

可以看到该可执行文件的两个逻辑分支的最后都是通过 nodejs 来执行 ../vite/bin/vite.js 这个 js 文件。

npm run command 过程

先看流程图,如下:

彻底搞懂 npm run 原理前言 这是 Vite 源码系列的开篇,在该系列中我们会根据 Vite 流程来阅读它的源码。

主要流程有以下几个阶段。

  1. 解析命令行,获取 npm run 后面的 command

  2. 读取 package.json 文件的内容。根据第一步获取的 command 来匹配 package.jsonscripts对象,拿到需要执行的命令。

  3. 将当前目录下的 node_modules/.bin 文件夹路径临时加入到 Path 环境变量中这样就可以在命令行中输入和可执行文件同名的命令来执行可执行文件。临时加入的环境变量只会在 npm run 运行期间有效,不会影响系统真正的 Path 环境变量。

关于临时将node_modules/.bin加入到 Path 环境变量看这里npm run script

In addition to the shell's pre-existing PATH, npm run adds node_modules/.bin to the PATH provided to scripts.

  1. 执行第二步拿到的命令,当前目录下 node_modules/.bin 文件夹下同名的可执行文件被执行,接着相应包中的 js 文件被执行。

用 nodejs 脚本模拟 npm run command

根据上面的几个步骤,我们写一个 nodejs 的脚本来模拟 npm run command 的过程。

新建文件夹 npmRun,进入 npmRun 文件夹通过初始化命令 npm init 初始化项目,此时会在当前文件夹下生成一个 package.json 文件。

接着通过命令 npm install --save-dev vite@latestVite 局部安装到 npmRun 文件夹。

我们在nomRun的根目录添加一个 index.html 文件。因为Vite 在做开发服务器时会默认启动一个以当前文件夹为根目录的服务,所以如果能在浏览器中正常访问我们添加的 html 文件,则说明 Vite 启动成功了。

接着在 package.json 文件中的 scripts 对象上添加 dev: vite --port 8090属性,这里我们通过 --port 选项设置 vite 服务的端口号为 8090

最后新建一个 npm.js 文件,这个文件就是我们模拟 npm run command 过程的 nodejs 脚本。

到这我们的准备工作已经做完,接下来开始写 nodejs 脚本。

首先我们需要用到 nodejs 两个方法。fs模块的 readFileSync 用来读取 package.json 文件内容,child_process 模块的 exec 用来执行命令行命令。 引入模块:

const { readFileSync } =  require("node:fs")
const { exec } = require('child_process');

通过 process 对象的 argv 获取命令行中的参数 dev

const argv = process.argv;
const scriptKey = argv[3];

读取 package.json 文件,根据字符串 "dev",找到对应的 script

const packageString = readFileSync("package.json", "utf-8")
const packageObj = JSON.parse(packageString)
const scriptValue = packageObj.scripts[scriptKey]

将 node_modules/bin 加入到 PATH

const Path = process.env.Path;
const tempPath = __dirname + "/node_modules/.bin"
process.env.Path = Path + ";" + tempPath

执行字符串 "dev" 对应的命令

exec(scriptValue, (err, stdout, stderr) => {
  console.log(stderr)
})

脚本编写完成,我们现在需要当前目录的命令行中输入 node npm.js run dev,然后在浏览器中输入 localhost:8090/index.html 查看能否正常访问页面。

至此我们就找到了 Vite 启动的入口文件。下一节我们将探索 vite/bin/vite.js 这个文件到底做了什么。

补充

既然最终是执行 node_modules/vite/bin/vite.js 这个文件,那么我们是否可以直接在命令行用 node 来执行这个文件呢?当然是可以的。在当前目录下打开命令行,输入 node ./node_modules/vite/bin/vite.js 可以看到 Vite 服务也能正常启动起来。既然可以直接调用,为什么还要用 npm run command呢?因为 npm run command 提供了一些额外的功能,比如命令的串行和并行。

转载自:https://juejin.cn/post/7290835160537481277
评论
请登录