概述
- 运行npm install命令的时候会发生什么?
- npm run xxx 到底执行了什么?
- package.json和package-lock.json的关系
1. 运行npm install命令的时候会发生什么?
- npm install命令输入
- 根据package.json包里的依赖去 检查node_modules目录下是否存在指定的依赖
- 如果已经存在则不必重新安装 ,若不存在,继续下面的步骤
- 向 registry(本地电脑的.npmrc文件里有对应的配置地址)查询模块压缩包的网址
- 下载压缩包,存放到根目录里的.npm目录里
- 解压压缩包到当前项目的node_modules目录中。
补充:几种不同的install命令下载方式
npm install xxx(依赖包)安装依赖模块至项目node_modules目录下,不会修改package.json文件里的内容
npm install -g xxx 安装依赖模块到全局(而不是项目node_modules目录下),不会将该依赖模块写到package.json文件里的dependencies和devDependencies字段里
npm install --save xxx #安装依赖模块到项目node_modules目录下,并将依赖写入到package.json文件里的dependencies字段中;该依赖是开发和生产环境里都需要的
npm install --save-dev xxx #安装依赖模块到项目node_modules目录下,并将依赖写入到package.json文件里的devDependencies字段中
2. npm run xxx 到底执行了什么呢?
Q1:npm run xxx到底执行了什么?
A1:去package.json文件里找scripts里找对应的参数xxx ,然后执行xxx的命令,例如启动react项目 npm run start的时候,实际上是执行了 icejs start 这条命令。为什么不直接执行 icejs start,因为直接执行会报错,操作系统中没有存在 icejs 这一条命令。
Q2:为什么执行 npm run start 的时候,这样它能成功,不报指令错误呢?
A2:我们在安装依赖的时候,是通过npm i xxx来执行的,我们在安装一条命令 例如:npm i @ice.js,npm在安装这个依赖的时候就会在node-modules/.bin/ 目录下创建好 icejs 为名的几个可执行文件,
.bin 目录,这个目录下不是任何一个npm包,这是一个软链接,打开文件可以看到文件顶部写着#!/bin/sh,表示这是一个脚本。由此我们可以知道,当使用 npm run start 执行 icejs start 时,虽然没有安装 icejs 的全局命令,但是 npm 会到 ./node_modules/.bin 中找到 icejs 文件作为 脚本来执行,则相当于执行了 ./node_modules/.bin/ icejs start.

Q3:.bin 目录下的文件表示软连接,那这个bin目录下的那些软连接文件是哪里来的呢?它又是怎么知道这条软连接是执行哪里的呢?
A:可以看到,它存在项目最外层的package-lock.json文件中
从 package-lock.json 中可知,当我们npm i 整个新建的vue项目的时候,npm 将 bin/ice-cli.js 作为 bin 声明了。
所以在 npm install 时,npm 读到该配置后,就将该文件软链接到 ./node_modules/.bin 目录下,而 npm 会自动把node_modules/.bin加入$PATH变量中,这样就可以直接作为命令运行依赖程序和开发依赖程序,不用全局安装了。
假如我们在安装包时,使用 npm install -g xxx 来安装,那么会将其中的 bin 文件加入到全局,比如 create-react-app 和 vue-cli ,在全局安装后,就可以直接使用如 vue-cli projectName 这样的命令来创建项目了。
也就是说,npm i 的时候,npm 就帮我们把这种软连接配置好了,其实这种软连接相当于一种映射,执行npm run xxx 的时候,就会到 node_modules/bin中找对应的映射文件,然后再找到相应的js文件来执行。

3. package.json和package-lock.json的关系
package.json 用来描述项目及项目所依赖的模块信息。package-lock.json 它会 在 npm 更改 node_modules 目录树 或者 package.json 时自动生成的 ,它准确的描述了当前项目npm包的依赖树,并且在随后的安装中会根据 package-lock.json 来安装,保证是相同的一个依赖树,不考虑这个过程中是否有某个依赖有小版本的更新。它的产生就是来对整个依赖树进行版本固定的(锁死)。
场景:依赖A依赖于依赖C-1.0版本,依赖B依赖于依赖C-2.0版本;而在执行npm install命令的时候,会按照package.json里面的依赖顺序依次解析,因此依赖C-1.0和依赖C-2.0的在文件里的放置顺序会导致node_modules的依赖结构产生变化。而且为了让开发者可以使用最新的依赖包,package.json文件里通常只会锁定大版本(即文件里依赖如果是^1.1.0版本,npm就会去仓库中获取符合1.x.x形式的最新版本),因此某些依赖包小版本更新后,也会造成依赖结构的改变。
当我们在一个项目中npm install时候,会自动生成一个package-lock.json文件,和package.json在同一级目录下。package-lock.json记录了项目的一些信息和所依赖的模块。这样在每次安装都会出现相同的结果. 不管你在什么机器上面或什么时候安装。
当我们下次再npm install时候,npm 发现如果项目中有 package-lock.json 文件,会根据 package-lock.json 里的内容来处理和安装依赖而不再根据 package.json。
package-lock.json文件可以保证每次执行npm install后生成的node_modules目录结构一定是完全相同的。
package-lock.json 文件还有个优点,就是它会缓存每个包的具体版本和下载链接,在后期再去install的时候,就不需要再去远程仓库进行查询操作了,减少了大量网络请求。