// https://www.jianshu.com/p/818833b2dd5a
npm的版本号管理
一个版本号分为三个部分: X, Y, Z. X表示主版本号, X为主版本号, Y为次版本号, Z为更新补丁号,
如果做稍微改动、 修复功能, 没有添加新功能, 更新Z, 如果添加新功能, 更新版本号Y, 如果有大的功能需要改动, 更新版本号X
~ 会匹配最近的小版本依赖包, 比如~ 1.2.3 会匹配所有1.2. x版本, 但是不包括1. 3.0
^ 会匹配最新的大版本依赖包, 比如 ^ 1.2.3 会匹配所有1. x. x的包, 包括1. 3.0, 但是不包括2. 0.0
使用场景
运行同一个项目, 补丁的版本更新, 使用不同项目的人npminstall, 二个不同的版本运行的结果可能不一样
这时候 package - lock. json 为了解决这个问题,( npm 版本在5以上), 内容处理和安装依赖, 他表明了
版本号, 获取地址和哈希值, 是的每次安装都是相同的结果
//https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/22
// npm 模块安装机制:
发送npm install的命令
查询node_modules目录是否有指定模块,
如果存在, 不需要重新安装
如果不存在
npm向registry查询压缩包的网址
下载压缩包, 存放在根目录下的. npm的目录里
解压压缩包到当前项目node_modules目录下
// npm 实现的原理
当输入npm install会经历如下几个阶段
1, 执行工程自身preinstall
2, 确定首层依赖模块
首先需要做的是确定工程中的首层依赖, 也就是 dependencies 和 devDependencies 属性中直接指定的模块( 假设此时没有添加 npm install 参数)。
工程本身是整棵依赖树的根节点, 每个首层依赖模块都是根节点下面的一棵子树, npm 会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。
3, 获取模块
获取模块的信息
在下载一个模块之前, 首先要确定其版本, 这是因为 package. json 中往往是 semantic version( semver, 语义化版本)。
此时如果版本描述文件( npm - shrinkwrap. json 或 package - lock. json) 中有该模块信息直接拿即可, 如果没有则从仓库获取。
如 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 过程中被去
5, 安装模块
这一步将会更新工程中的 node_modules, 并执行模块中的生命周期函数( 按照 preinstall、 install、 postinstall 的顺序)。
6, 执行工程中自身生命周期
当前 npm 工程如果定义了钩子此时会被执行( 按照 install、 postinstall、 prepublish、 prepare 的顺序)。
最后一步是生成或更新版本描述文件, npm install 过程完成。