Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术随笔

  • 转载文章

  • Mac相关

  • 前端学习周报

  • 包管理工具

    • package.json中^和~的含义
    • npm与pnpm的拓扑结构
      • 0.前言
      • 1.module 的查找方式:向上查找
      • 2.npm2 的依赖结构
      • 3.npm3 的依赖结构
      • 4.pnpm的依赖结构
      • 参考资料
    • 软链接与硬链接
    • 如何将自己的package发布到npm官方仓库
    • npm link的用法
    • npx使用场景
  • 技术随笔
  • 包管理工具
wangjiasheng
2022-02-17
目录

npm与pnpm的拓扑结构

# 0.前言

本篇博客主要讲的是:不同包管理工具形成node_modules拓扑结构的变化。

在这一节就可以明显的发现最近大火的pnpm相比于前代包管理(npm/yarn)工具上在资源空间上的优势,创造性的使用hardlink和softlink来链接module与module之间的引用。

此外,讲解pnpm的时候,需要有软链接和硬链接的前提知识,这个可以看我的另一篇文章《软链接与硬链接》。

# 1.module 的查找方式:向上查找

当 require('package-hello') 时,假设 package-hello 是一个 npm 库,我们是如何找到该 package 的?

  1. 寻找当前目录的 node_modules/package-hello 目录
  2. 如果未找到,寻找上一级的 ../node_modules/package-hello 目录,以此递归查找。

# 2.npm2 的依赖结构

在 npmv2 时,node_modules 对于各个 package 的拓扑为嵌套结构。

假设:

  1. 项目依赖 package-a 与 package-b 两个 package
  2. package-a 与 package-b 均依赖 lodash@4.17.4

依赖关系以 Markdown 列表表示:

+ package-a
  + `lodash@4.17.4`
+ package-b
  + `lodash@4.17.4`
1
2
3
4

此时 node_modules 目录结构如下:

image-20220217205125333

此时最大的问题:

  1. 嵌套过深。
  2. 占用空间过大。

# 3.npm3 的依赖结构

目前在 npm/yarn 中仍然为平铺结构,但 pnpm 使用了更省空间的方法,以后将会提到。

扁平/平铺的含义:第三方依赖和第三方依赖的依赖会被安装在同级。

在 npmv3 之后 node_modules 为平铺结构,拓扑结构如下:

image-20220217205222349

问题:以下依赖最终node_modules的结果是什么?

可参考该示例 (opens new window)

依赖关系以 Markdown 列表表示:

+ package-a
  + `lodash@^4.17.4`
+ package-b
  + `lodash@^4.16.1`
1
2
3
4

答: 与上图所示的拓扑结构一致,因为二者为 ^ 版本号,他们均会下载匹配该版本号范围的最新版本,即 @4.17.4,因此二者依赖一致。


如果考虑 package.lock.json文件的话,会严格按照package.json中指定的版本规则,此时拓扑结构会变成:

image-20220217205723089

此时,npm3拓扑结构的问题就差不多出现了,即当存在两个相同module,而不同version时。

即,当第一个同名的module会被扁平的安装在与dependency同级的目录,其余version的module则会和npm2一样嵌套的安装在依赖下方。

如,假设存在依赖:

+ package-a
  + `lodash@4.0.0`
+ package-b
  + `lodash@4.0.0`
+ package-c
  + `lodash@3.0.0`
+ package-d
  + `lodash@3.0.0`
1
2
3
4
5
6
7
8

答:package-d 只能从自身的 node_modules 下寻找 lodash@3.0.0,而无法从 package-c 下寻找,此时 lodash@3.0.0 不可避免地会被安装两次

node_modules 目录结构如下图:

# 4.pnpm的依赖结构

那么不可避免地在 npm 或者 yarn 中,lodash@3.0.0 会被多次安装,无疑造成了空间的浪费与诸多问题。

这是一个较为常见的场景,在平时项目中有些库相同版本甚至会安装七八次,如 postcss、ansi-styles、ansi-regex、braces 等。

而在 pnpm 中,它改变了 npm/yarn 的目录结构,采用软链接+硬链接的方式,避免了 doppelgangers 问题,更加节省空间。

假设在package.json中存在以下依赖:

+ bar@1.0.0
+ bar
  + `foo@1.0.0` #(且foo之前已经被别的依赖在同级安装过了)
1
2
3

pnpm的目录拓扑结构如下所示:

pnpm在安装bar依赖时,会在node_modules目录下会生成两个文件:

# 1. node.js正常寻找的目录
|-- node_modules/bar  # 全部存放着软链接

# 2. modulde真正存放的位置
|-- node_modules/.pnpm/bar@1.0.0/node_modules/A  # 硬链接
1
2
3
4
5

从图中可以发现,当 foo 遇到其余同名其他 version 的依赖时,会把多个version 版本的module安装在.pnpm这一层级上,并以hardlink的方式与真实硬件存放的.pnpm store链接在一起,如图中的foo@1.0.0、foo@2.0.0。

当发现重复的依赖时,如foo@1.0.0,则会以软链接的方式与第一层的foo@1.0.0链接起来。

此时,会有同学存在一个问题,为啥嵌套的依赖不以hardlink方式与.pnpm store链接起来呢?

这部分我也不是很清楚,详见:https://pnpm.io/blog/2020/05/27/flat-node-modules (opens new window)-is-not-the-only-way (opens new window)

# 参考资料

  1. 【Q720】请描述 node_modules 的目录结构(拓扑结构) #746 (opens new window)

  2. 【Q725】pnpm 有什么优势 (opens new window)

  3. NPM doppelgangers (opens new window)

编辑 (opens new window)
#npm
上次更新: 2022/04/06, 15:04:00
package.json中^和~的含义
软链接与硬链接

← package.json中^和~的含义 软链接与硬链接→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式