Skip to content

npm run命令执行过程

1. npm依赖的种类有那些?

在开发中,项目发package.json文件中,常见的或常用的就是开发依赖devDependencies和生产依赖dependencies,分别通过--save-dev--save来进行安装,除了这两种依赖之外,其他还有一些不常用的依赖,例如 同等依赖,打包依赖,可选依赖,等

  • devDependencies:开发依赖 仅在开发阶段会使用的依赖,打包时候不会被打包,例如一些loader
  • dependencies:生产依赖 仅在生产环境下需要的依赖,可通过npm install --production来下载所有的生产依赖,例如nuxt上线后,仅仅只需要生产依赖。
  • peerDependencies:同等依赖 可有可无,主要作用是用来在安装时候进行提示用的。例如:bootstrap依赖于jquery,那么,可以在peerDependencies内放入jquery,用户在安装bootstrap的时候,就知道需要安装jquery了;
  • optionalDependencies:可选依赖 可有可无,也是做提示用的。
  • bundledDependencies:捆绑依赖(npm压缩) 主要作用是在npm pack压缩项目时候,指定需要将那些node_modules中的依赖一起压缩,若没有指定任何依赖,则打包后的压缩包内不包含node_modulesbundledDependencies:['dayjs'],其值为数组

使用npm pack 打包tgz时会将捆绑依赖一同打包

image.png

npm pack打的压缩包,可以直接解压使用

注意:npm i 在安装依赖的时候只会下载开发依赖和生产依赖,其他的三个依赖不会自动下载

2. .bin目录是干什么的?

在安装好依赖后,node_modules目录下会多出一个.bin目录,这个目录不属于任何的依赖包,也不是依赖项,里面存放的是一些可执行脚本文件,.bin目录的意义意味着你安装的一些模块可以在命令行中执行。

但是,要区分是全局安装还是项目安装,

  • 若是全局安装,会在全局的node_modules目录下的.bin中新增脚本,
  • 若是项目安装,是在项目的node_modules下的.bin中新增脚本文件

全局安装的可以直接在命令行执行,项目安装的必须得npm run xxx,且需要配置scripts脚本

为什么呢?

对于全局依赖,由于npm已经在环境变量里面了,所以npm下面的目录可以全局执行(另一篇文章有详细介绍) 对于项目依赖,下面有介绍

image.png

3. npm run xxx 时候做了什么?

我们知道,安装到项目的命令依赖不能直接在命令行执行,得通过npm run xxx才能执行,且必须在scripts内先配置相应的脚本启动命令,这是为何呢?

上面说过,项目的node_modules.bin目录下存放着一堆脚本文件,我们在执行npm run xxx命令的时候,其实就是执行项目的node_modules目录下的.bin中某个xxx脚本,那只需要搞清楚,npm run的时候是咋能找到.bin中的脚本这个问题,就知道npm run xxx的原理了。

3.1 npm run env

首先,我们要知道npm run env这个命令的作用,可以将全局现有的环境变量打印出来,且这个命令不属于某个项目内配置的scripts脚本。

image.pngimage.png

可以看到,运行了npm run xxx命令时,做的事情就是设置环境变量,并且很重要的一点,就是将当前所在目录(global-module)的node_modules下的.bin目录也加入到系统path环境变量下面,这就是为何在npm run xxx命令的时候,.bin目录下面的xxx命令都可以能被找到并执行的原因。

但是,这个这仅仅在npm run时候才生效,当执行完npm run命令后,就会自动从环境变量中删除刚刚添加进去的node_modules下面的.bin这个环境变量,可以直接在命令行运行path来验证,path能打印出当前计算机内的paht内的环境变量;

image.png

可以看到,系统内是没有node_modules/.bin这个环境变量了,再执行.bin下面的xxx脚本,就会报xxx不是内部或外部命令,也不是可运行的程序或批处理文件。

npm run去执行一个脚本,若这个脚本对应的包在项目内安装了,就如上面所说,会根据零时环境变量,先找项目的node_modules下的.bin目录内的脚本,若是项目内没有安装,但是全局安装了,那么就会在环境变量中找到全局的node_modules下的`对应包来执行。

3.2 prepost 命令

npm的scripts脚本中, 除了自定义脚本名称外, 还提供了两个前置(pre)与后置(post)钩子命令, 把这俩命令拼接到自定义命令前面, 就可以实现前置或后置钩子指令了,例如:

json
"scripts":{
    "predev":"node ./predev.js",
    "dev": "vite",
    "postdev": "node ./postdev.js",
}

这样运行 npm run dev 指令的时候,终端会自动先运行 npm run predev ,再运行 npm run dev ,最后运行 npm run postdev,通过这种方式,我们就可以在自定义脚本运行前,执行一些前置或者后置操作, 比如先对包管理器进行判断, 然后强制开发者使用哪种包管理器。

4. npx xxx

npx命令是npm v5.2之后引入的新命令,npx可以帮我们直接执行node_modules/.bin文件夹下的文件

npx 运行脚本核心原理和 npm 类似,但是npx有一点不一样,就是在执行npx脚本时候,若包不存在,就会先下载,然后通npm一样运行下载的包,等脚本执行完毕又自动删除刚刚下载的包。

  1. 执行脚本
shell
npx webpack

省去了配置scripts脚本

  1. npx避免了安装全局模块 全局安装的模块会带来很多问题,例如:多个用户全局安装的模块版本不同
shell
npx create-react-app react-project

我们可以直接使用 npx 来执行模块,它会先进行安装,安装执行后会将下载过的模块删除~,这样可以一直使用最新版本啦~

5. scripts配置

package.json中可以定义自己的脚本通过npm run来执行

json
"scripts": {
   "hello": "echo hello",
   "build": "webpack"
}

我们可以使用 npm run hello执行脚本,也可以使用 npm run build执行node_modules/.bin目录下的webpack文件

  • npm run 命令执行时,会把 ./node_modules/.bin/ 目录添加到执行环境的 PATH 变量中,因此如果某个命令行包未全局安装,而只安装在了当前项目的 node_modules 中,通过 npm run 一样可以调用该命令。
  • 执行 npm 脚本时要传入参数,需要在命令后加 -- 标明, 如 npm run hello -- --port 3000 可以将 --port 参数传给hello 命令`
  • npm 提供了 prepost 两种钩子机制,可以定义某个脚本前后的执行脚本,没有定义默认会忽略
json
"scripts": {
   "prehello":"echo prehello",
   "hello": "echo hello",
   "posthello":"echo posthello"
}

可以通过打印全局env和 在项目下执行npm run env来对比PATH属性,不难发现在执行npm run 的时候确实会将 ./node_modules/.bin/ 目录添加到PATH

6. 协议

这张图就说明了为什么MIT许可是最大的了

image.png