
开门见山,npm install 大概会经过上面的几个流程,本篇文章来讲一讲各个流程的实现细节、发展以及为何要这样实现。
# 嵌套结构
我们都知道,执行 npm install 后,依赖包被安装到了 node_modules ,下面我们来具体了解下,npm 将依赖包安装到 node_modules 的具体机制是什么。
在 npm 的早期版本, npm 处理依赖的方式简单粗暴,以递归的形式,严格按照 package.json 结构以及子依赖包的 package.json 结构将依赖安装到他们各自的 node_modules 中。直到有子依赖包不在依赖其他模块。
举个例子,我们的模块 my-app 现在依赖了两个模块:buffer、ignore:
{
"name": "my-app",
"dependencies": {
"buffer": "^5.4.3",
"ignore": "^5.1.4",
}
}
@前端进阶之旅: 代码已经复制到剪贴板
ignore是一个纯 JS 模块,不依赖任何其他模块,而 buffer 又依赖了下面两个模块:base64-js 、 ieee754。
{
"name": "buffer",
"dependencies": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
}
@前端进阶之旅: 代码已经复制到剪贴板
那么,执行 npm install 后,得到的 node_modules 中模块目录结构就是下面这样的:

这样的方式优点很明显, node_modules 的结构和 package.json 结构一一对应,层级结构明显,并且保证了每次安装目录结构都是相同的。
但是,试想一下,如果你依赖的模块非常之多,你的 node_modules 将非常庞大,嵌套层级非常之深:

- 在不同层级的依赖中,可能引用了同一个模块,导致大量冗余。
- 在
Windows系统中,文件路径最大长度为260个字符,嵌套层级过深可能导致不可预知的问题。
# 扁平结构
为了解决以上问题,NPM 在 3.x 版本做了一次较大更新。其将早期的嵌套结构改为扁平结构:
- 安装模块时,不管其是直接依赖还是子依赖的依赖,优先将其安装在
node_modules根目录。
还是上面的依赖结构,我们在执行 npm install 后将得到下面的目录结构:


此时我们若在模块中又依赖了 base64-js@1.0.1 版本:
{
