深挖 NPM 机制
使用NPM安装的时候会经常出现包冲突(比如多个主模块的子模块版本不一致等),导致在开发过程中会遇到各种或大或小的问题。所有在这会介绍以下内容:
- NPM 主要安装方式
- NPM 包信息查询
- NPM 安装机制(主要)
安装&查询命令
NPM 各种安装方式
npm install packageName[@next | @versionNumber]- 在 node_modules 中没有指定模块时安装,(不检查~/.npm目录)
npm install packageName --f | -- force- 一个模块不管是否安装过,npm都要 强制重新安装
npm update packageName- 如果远程版本较新、或者本地版本不存在时安装
NPM 查询服务
- NPM通过registry的查询服务,从而知道每个模块的最新版本。
- 可以通过
npm view packageName [version]查询对映模块的信息
NPM 安装机制
输入 npm install 命令并敲下回车后,会经历如下几个阶段(以 npm 5.5.1 为例):
1. 执行工程自身 preinstall
当前 npm 工程如果定义了 preinstall 钩子此时会被执行。
2. 确定首层依赖模块
首先需要做的是确定工程中的首层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加 npm install 参数)。
工程本身是整棵依赖树的根节点,每个首层依赖模块都是根节点下面的一棵子树,npm 会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。
如果查询node_modules目录之中已经存在指定模块,那么不再重新安装
3. 获取模块
获取模块是一个递归的过程,分为以下几步:
- 获取模块信息
- 在下载一个模块之前,首先要确定其版本,这是因为 package.json 中往往是 semantic version(semver,语义化版本)
- 此时如果版本描述文件(npm-shrinkwrap.json 或 package-lock.json)中有该模块信息直接拿即可
- 如果没有则从仓库获取(向registry查询)。如 packaeg.json 中某个包的版本是 ^1.1.0,npm 就会去仓库中获取符合 1.x.x 形式的最新版本。
- 获取模块实体。
- 上一步会获取到模块的压缩包地址(resolved 字段),npm 会用此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。
- 查找该模块依赖
- 如果有依赖则回到第1步,如果没有则停止。
4. 模块扁平化(dedupe)
一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。比如 A 模块依赖于 loadsh,B 模块同样依赖于 lodash。在 npm3 以前会严格按照依赖树的结构进行安装,因此会造成模块冗余。
从 npm3 版本 开始默认加入了一个 dedupe 的过程。它会遍历所有节点,逐个将模块放在根节点下面,也就是 node-modules 的第一层。当发现有重复模块时,则将其丢弃。
这里需要对重复模块进行一个定义,它指的是模块名相同且 semver 兼容。每个 semver 都对应一段版本允许范围,如果两个模块的版本允许范围存在交集,那么就可以得到一个兼容版本,而不必版本号完全一致,这可以使更多冗余模块在 dedupe 过程中被去掉。
比如
- node-modules 下 foo 模块依赖 lodash@^1.0.0,bar 模块依赖 lodash@^1.1.0,则 ^1.1.0 为兼容版本。
- 而当 foo 依赖 lodash@^2.0.0,bar 依赖 lodash@^1.1.0,则依据 semver 的规则,二者不存在兼容版本。会将一个版本放在 node_modules 中,另一个仍保留在依赖树里。
举个例子,假设一个依赖树原本是这样:
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 的顺序)。
最后一步是生成或更新版本描述文件,npm install 过程完成。
End
文章分享同步于: https://github.com/zhongmeizhi/gitbook-FED
参考
深挖 NPM 机制的更多相关文章
- 深挖JDK动态代理(一)
最近在研究RPC框架,避免不了的就是在RPC调用中使用最多的则是动态代理的机制了,基于此,我们先来研究一下JDK动态代理 我们先来尝试着编写一下JDK动态代理的代码 1. 由于JDK动态代理是基于接 ...
- 深挖Openstack Nova - Scheduler调度策略
深挖Openstack Nova - Scheduler调度策略 一. Scheduler的作用就是在创建实例(instance)时,为实例选择出合适的主机(host).这个过程分两步:过滤(F ...
- 【转帖】深挖NUMA
深挖NUMA http://www.litrin.net/2017/10/31/深挖numa/ 首先列出本站之前相关的几篇帖子: Linux的NUMA机制 NUMA对性能的影响 cgroup的cpus ...
- 深挖JDK动态代理(二):JDK动态生成后的字节码分析
接上一篇文章深挖JDK动态代理(一)我们来分析一下JDK生成动态的代理类究竟是个什么东西 1. 将生成的代理类编程一个class文件,通过以下方法 public static void transCl ...
- 深挖的Java源代码之Integer.parseInt()vs Integer.valueOf()
Integer.parseInt()和Integer.valueOf()都是用来将String转换为Int的,但是为什么Java会提供两个这样的方法呢,他们如果是同样的操作,岂不是多此一举? 我们来深 ...
- 深挖计算机基础:MySQL实战45讲学习笔记
参考极客时间专栏<MySQL实战45讲>学习笔记 一.基础篇(8讲) MySQL实战45讲学习笔记:第一讲 MySQL实战45讲学习笔记:第二讲 MySQL实战45讲学习笔记:第三讲 My ...
- 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01
百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...
- EntityFramework Core高并发深挖详解,一纸长文,你准备好了吗?
前言 之前有关EF并发探讨过几次,但是呢,博主感觉还是有问题,为什么会觉得有问题,其实就是理解不够透彻罢了,于是在项目中都是用的存储过程或者SQL语句来实现,利用放假时间好好补补EF Core并发的问 ...
- EntityFramework Core并发深挖详解,一纸长文,你准备好看完了吗?
前言 之前有关EF并发探讨过几次,但是呢,博主感觉还是有问题,为什么会觉得有问题,其实就是理解不够透彻罢了,于是在项目中都是用的存储过程或者SQL语句来实现,利用放假时间好好补补EF Core并发的问 ...
随机推荐
- 基于思科模拟器的AAA配置与验证
拓扑图: 地址表如图所示 三个路由器之间采用ospf协议达到互通 先做ping通测试 由ApingB 由ApingC 配置AAA认证 在R1上 R1(config)#username shuaiqiy ...
- Java语言编程 - Java第一个程序HelloWorld
3.1 新建Java文件 首先新建一个文件夹,用于存放写的Java程序,例如我存放Java程序的位置为” D:\Files\code\java”. 在该文件夹中,右键新建一个文本文档 将文件名重命名为 ...
- ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露
一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...
- java数据结构和算法01(数组的简单使用)
一直都对这一块没有什么想法,加上不怎么理解,只是懂个大概:最近突然感觉对数据结构和算法这块有点儿兴趣,决定还是尽量详细的看看这些结构和算法: 话说什么事数据结构和算法呢?现在我也说不上来,等我学的差不 ...
- Asp.NetCore轻松学-部署到 Linux 进行托管
前言 上一篇文章介绍了如何将开发好的 Asp.Net Core 应用程序部署到 IIS,且学习了进程内托管和进程外托管的区别:接下来就要说说应用 Asp.Net Core 的特性(跨平台),将 .Ne ...
- 基于CNN的人群密度图估计方法简述
人群计数的方法分为传统的视频和图像人群计数算法以及基于深度学习的人群计数算法,深度学习方法由于能够方便高效地提取高层特征而获得优越的性能是传统方法无法比拟的.本文简单了秒速了近几年,基于单张图像利用C ...
- 一个非常简单的IMPDP事儿
EXPDP出来的DMP文件包含2个Schema的表,现在要IMPDP到一个Schema里面试了几把都报错,好久不用逻辑导出入,折腾了好久,出现各种错误1.创建目录并授权create or replac ...
- python word转pdf
原理 使用python win32 库 调用word底层vba,将word转成pdf 安装pywin32 pip install pywin32 python代码 from win32com.clie ...
- vue学习笔记2
Vue.js - Day2 品牌管理案例 添加新品牌 删除品牌 根据条件筛选品牌 1.x 版本中的filterBy指令,在2.x中已经被废除: filterBy - 指令 <tr v-for=& ...
- Python后台开发Django(启动)
Django版本为:2.1.7 Python的web框架,MTV思想 MVC Model(模板文件,数据库操作) view(视图模板文件 )controller(业务处理) MTV Model(模 ...