前端项目中运行 npm run xxx 的时候发生了什么?

161 阅读4分钟

npm 是 node 捆绑的依赖管理器,常用程度可想而知。那么你每天都在 npm/yarn run 的命令到底是如何运行项目的呢?

前端项目中运行 npm run xxx 的时候发生了什么? 大家都知道目前的 node 是捆绑 npm 的。npm 是 node 的依赖管理器,虽然它不是唯一的选择,我们还有 pnpm/yarn/cnpm/ni 。

但是,的依赖管理器都是在解决 npm 的某个痛点。对于 npm 依赖声明文件 ​ ​package.json​ ​ 本身是基本没有变化的。

例如我们可以使用 ​ ​npm run serve​ ​ 运行某个命令, 也可以使用 ​ ​yarn serve​ ​ 运行某个命令。

可以看到在这个地方 yarn 可以省略 run 这个参数。

但是,他们都只是对 ​ ​package.json​ ​ 进行解析而已,例如下面的文件,当运行 ​ ​npm run serve​ ​ 时,其实就是运行该 json 文件中的 ​ ​scripts​ ​ 下的 ​ ​serve​ ​ 键对应的命令。

{
    "name": "h5",
    "version": "1.0.7",
    "private": true,
    "scripts": {
        "serve": "vue-cli-service serve"
      },
    "dependencies": {
        "axios": "^0.19.2",
        "vuex": "^3.4.0"
      },
    "devDependencies": {
        "node-sass": "^4.12.0"
      }
}

上面说是 ​ ​命令​ ​ 只是用于方便理解,例如:

npm run server
# 类似于在命令行运行以下命令
vue-cli-service serve

通过 npm run 与直接运行命令的区别

还是用上面的配置来描述:

{
"scripts": {
    "serve": "vue-cli-service serve"
  }
}

npm 在运行 ​

vue-cli-service serve​ ​ 这条命令的时候,会先在当前 ​ ​node_modules/.bin​ ​ 下面看有没有同名的可执行文件,如果有,则使用其运行。

这里我们可以打开这个目录看看: node_modules/bin中 有三个vue-cli-service文件

image.png

如果直接在命令行中运行 ​ ​vue-cli-service serve​ ​ 这条命令,是不会从 node_modules 中查找可执行程序的。

运行可执行文件

那么什么叫可执行文件呢?上面的图中有很多个同名的 vue-cli-service ,到底是运行哪个?

我们先来分析这几个文件怎么来的?

例如 ​ ​@vue/cli-service​

​ 有以下 ​ ​package.json​ ​ 文件,当我们运行 ​ ​npm i @vue/cli-service​ ​ 这条命令时,npm 就会在 ​ ​node_modules/.bin/​ ​ 目录中创建好以 ​ ​vue-cli-service​ ​ 为名的几个可执行文件了。

再看 package-lock.json 中可知,当我们npm i 整个新建的vue项目的时候,npm 将 bin/vue-cli-service.js 作为 bin 声明了。

1665458624(1).jpg

对于​ ​可执行​ ​这个定义,每个系统不一样。在 windows 系统上,可执行文件是通过组策略和环境变量决定的。

使用 ​ ​set pathext​ ​ 可以查看 ​ ​pathext​ ​ 这个环境变量,他定义了可以作为可执行文件的后缀。

查看可执行文件后缀

在 unix 系统上面,是通过设置文件的属性为可执行,再在文件中的第一行声明解释器来运行的。

image.png

如果我们在 cmd 里运行的时候,windows 一般是调用了 ​ ​vue-cli-service.cmd​ ​ 这个文件,这是 windows 下的批处理脚本:

image.png

所以当我们运行 ​ ​vue-cli-service serve​ ​ 这条命令的时候,就相当于运行 ​ ​node_modules/.bin/vue-cli-service.cmd serve​ ​。

然后这个脚本会使用 node 去运行 ​ ​vue-cli-service.js​ ​ 这个 js 文件,由于 node 中可以使用一系列系统相关的 api ,所以在这个 js 中可以做很多事情,例如读取并分析运行这条命令的目录下的文件,根据模板生成文件等。

# unix 系默认的可执行文件,必须输入完整文件名
vue-cli-service

# windows cmd 中默认的可执行文件,当我们不添加后缀名时,自动根据 pathext 查找文件
vue-cli-service.cmd

# Windows PowerShell 中可执行文件,可以跨平台
vue-cli-service.ps1

这里多提了下,在 windows 中 cmd 脚本使用得比较多,兼容性也较好。 powerShell 虽然比较强大,但他运行命令的方式由于和 cmd 命令有较大不同,这会导致你常常搞不清什么命令应该在什么解释器里运行。

示例:运行命令的方式不兼容

示例:windows 很多系统会默认禁止此脚本运行,导致 npm 命令运行错误

所以如果遇到 powerShell 相关错误时建议用 cmd 试试。

总结

  • 运行 npm run xxx的时候,npm 会先在当前目录的 node_modules/.bin 查找要执行的程序,如果找到则运行;
  • 没有找到则从全局的 node_modules/.bin 中查找,npm i -g xxx就是安装到到全局目录;
  • 如果全局目录还是没找到,那么就从 path 环境变量中查找有没有其他同名的可执行程序。