-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
第 20 题:介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块? #22
Comments
建议可以把原理性知识和整个流程分开说明,这样整个流程的顺序就很清晰了。 流程性的知识可以作为一个框架,然后慢慢填充细节内容到里面。 如果理解流程顺序不清晰,其实对于debug实际问题无益处。 以上只是建议。 |
安装依赖的时候,获取完首层依赖,然后把每个依赖当做树的根节点,进行深度优先安装过程吗?还是是广度优先的?不同的安装方式,是不是对依赖版本冲突的解决也有影响? |
上一步会获取到模块的压缩包地址(resolved 字段),npm 会用此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。 有一个疑问,这里检查的是本地缓存 |
1、发出npm install命令; |
我也遇到这个问题,但是我还在苦苦寻找中 |
'查询node_modules目录之中是否已经存在指定模块' @sisterAn 请教一下,这里的查询,是只查询项目中package.json同级的目录呢还是会向父级目录逐层查询? |
回答npm install如何计算需要安装哪些包的。英文版原文来自:https://dev.to/shree_j/how-npm-works-internally-4012
|
之前面大厂的时候被问到这个问题,同事都说问的太细了,看来还是太嫩了 |
package.json 同级的目录 就会有 node_modules, @sisterAn 这里应该指的是所查询模块是否在当前目录下的node_modules, 如果在,就会在当前目录下找是否有package.json ,如果有package.json ,就会查找有没有index.js 文件,如果有就用index文件定义模块,如果没有index文件,还会检查package.json中是否有main元素,如果有那就用main元素指定的文件定义模块,如果没有就抛异常。 如果在当前目录下没有node_modules, 就会尝试进入父目录。如果父目录存在,就接着检查目录里是否有node_modules, 有则返回模块。 如果找了半天,父目录没有模块,或者干脆找到顶了,没有父目录了,这时就会是查找的最后一步,会检查由环境变量NODE_PATH 指定的目录下,也就是全局安装node_modules 所在的目录,如果还没有,就抛出异常,如果有,就返回它。 |
模块安装过程
依赖关系梳理
|
当 lock file 存在时,如果 lock file 记录的版本信息不需要更新,则会使用 lock file 记录的模块信息。 |
https://zhuanlan.zhihu.com/p/503702422 上面的讨论都 少一个对 bin 文件的处理过程 |
1. npm 模块安装机制:
npm install
命令.npm
目录里node_modules
目录2. npm 实现原理
输入 npm install 命令并敲下回车后,会经历如下几个阶段(以 npm 5.5.1 为例):
当前 npm 工程如果定义了 preinstall 钩子此时会被执行。
首先需要做的是确定工程中的首层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加 npm install 参数)。
工程本身是整棵依赖树的根节点,每个首层依赖模块都是根节点下面的一棵子树,npm 会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。
获取模块是一个递归的过程,分为以下几步:
上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。比如 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
这一步将会更新工程中的 node_modules,并执行模块中的生命周期函数(按照 preinstall、install、postinstall 的顺序)。
当前 npm 工程如果定义了钩子此时会被执行(按照 install、postinstall、prepublish、prepare 的顺序)。
最后一步是生成或更新版本描述文件,npm install 过程完成。
参考 npm 模块安装机制简介
详解npm的模块安装机制
npm install的实现原理
The text was updated successfully, but these errors were encountered: