npm模块安装机制

目录

1. npm模块安装机制

2.npm实现原理

1. 执行工程自身preinstall

2. 确定首层依赖模块

3. 获取模块

4.模块扁平化

5. 安装模块

6. 执行工程自身生命周期

7. 生成或更新版本描述文件,npm install过程完成

3.离线安装的解决方案

1. 一个目前仍有问题的方案

离线方案思路1 

离线方案思路2

离线方案思路3


1. npm模块安装机制

发出npm install命令

查询node_modules目录之中是否已存在指定模块

  • 若存在,不再重新安装,即使远程仓库中已经有了一个新版本,也是如此
  • 若不存在:
    •  npm向registry查询模块压缩包的网址
    • 下载压缩包,存放在根目录下的.npm目录里
    • 解压压缩包到当前项目的node_modules目录

如果你希望,一个模块不管是否安装过,npm都要强制重新安装,可以使用 -f  或  --force

npm install <packageName> --force

【补充】

1. npm update:更新已安装模块

①: 会先到远程仓库中查询最新版本:

npm模块仓库提供了一个查询服务,registry

②:然后查询本地版本,如果本地版本不存在,或者远程版本较新,就会安装。

2. 缓存目录:

npm install和npm update命令,从registry下载压缩包后,都会存放在本地的缓存目录,在Linux和Mac默认是用户主目录下的.npm目录,在windows是%AppData%/npm-cache

$ npm config get cache
$home/.npm

// 查看目录
$ ls ~/.npm 
# 或者
$ npm cache ls

每个模块的每个版本都有自己的一个子目录,里面的代码是压缩包package.tgz文件以及一个描述文件

除此之外,还会生成一个{cache}/{hostname}/{path}/.cache.json文件。比如,从 npm 官方仓库下载 react 模块的时候,就会生成registry.npmjs.org/react/.cache.json文件。这个文件保存的是所有版本的信息,以及该模块最近修改的时间和最近一次请求时服务器返回的ETag

2.npm实现原理

输入npm install命令后,会经历如下几个阶段:

1. 执行工程自身preinstall

当前工程如果定义了preinstall钩子,此时会被执行

2. 确定首层依赖模块

首先要做的是确定工程中的首层遗爱,也就是dependencies和dev-denpendencies属性中直接指定的模块。(假设此时没有添加npm install参数)

工程本身是整颗依赖树的根节点,每个首层依赖模块都是根节点下面的一颗子树,npm会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。

3. 获取模块

获取模块是一个递归的过程,分为以下几步:

  • 获取模块信息:在下载一个模块之前,首先确定其版本,因为package.json中往往是semantic version(语义化版本),此时如果版本描述文件(npm-shrinkwrap.json 或 package-lock.json)中有模块信息直接拿即可,如果没有从仓库获取。(如 packaeg.json 中某个包的版本是 ^1.1.0,npm 就会去仓库中获取符合 1.x.x 形式的最新版本)
  • 获取模块实体:上一步会获得模块的压缩包地址(resolved字段)npm会用此地址检查本地缓存,缓存中有就直接拿,没有则从仓库中下载。
  • 查找该模块依赖:如果有依赖则从头执行,如果没有,停止

4.模块扁平化

上一步获取的是一个完整的依赖树,其中可能包含大量的重复模块(比如 A 模块依赖于 loadsh,B 模块同样依赖于 lodash。)。在npm3之前会严格按照依赖树的结构进行安装,因此会造成模块冗余。

从npm3开始默认加入了一个dedupe的过程,会遍历所有节点,逐个将模块放在根节点下面,也就是node_modules的第一层,当发现有重复模块的时候,将其丢弃

重复模块:指模块名相同,且semver(语义化版本号)兼容,每一个semver都对应一段版本允许范围,如果两个模块的版本允许范围存在交集,就可以得到一个兼容版本。不必版本号完全一致,可以使得更多的冗余模块在dedupe过程中被去掉

node_modules
-- foo
---- lodash@version1

-- bar
---- lodash@version2

假设 version1 和 version2 是兼容版本,则经过 dedupe 会成为下面的形式:
node_modules
-- foo

-- bar

-- lodash(保留的版本为兼容版本)



假设 version1 和 version2 为非兼容版本,则后面的版本保留在依赖树中:
node_modules
-- foo
-- lodash@version1

-- bar
---- lodash@version2

5. 安装模块

这一步将更新工程中的node_modules,并执行模块中的生命周期函数(preInstall  install postInstall的顺序)

6. 执行工程自身生命周期

当前npm工程如果定义了钩子,此时将被执行(install postInstall prePublish prepare的顺序)

7. 生成或更新版本描述文件,npm install过程完成

3.离线安装的解决方案

一个模块安装完成后,本地保存了两份,一个是.npm中保存的压缩包,一个是node_Modules中的解压后的代码,但是在运行npm install的时候,只会去检查node_modules,也就是说,当一个模块在.npm下有压缩包,但是没安装在node_modules中,npm也会从远程仓库中下载一次。

会极大的影响安装速度,且在没有网络的环境下,即使本地中有安装包,也使用不了。

1. 一个目前仍有问题的方案

为解决上述问题,npm提供了一个--cache-min参数,用于从缓存目录中安装模块

--cache-min参数指定一个时间(分钟)只有超过这个时间的模块,才会从registry中下载

npm install --cache-min 9999999 <package-name>

npm install --cache-min Infinity <package-name>

但是这并不等与离线模式,仍然需要网络下载

如果指定模块在缓存目录之中,npm 也会连接 registry,发出指定模块的 etag ,服务器返回状态码304,表示不需要重新下载压缩包。

如果某个模块已经在缓存之中,但是版本低于要求,npm会直接报错,而不是去 registry 下载最新版本。

离线方案思路1 

registry代理,本机起一个registry服务,所有的npm install命令都会通过换个服务代理,能够完全实现缓存安装,离线使用

离线方案思路2

npm install替代,npm-cache替代npm install

离线方案思路3

将node_modules作为缓存目录

将项目的node_modules目录打包成一个压缩包,以后安装的时候就从这个压缩包中取出文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

this_is_Azou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值