从2009年 Node 诞生至今,Node 生态发展繁荣,围绕 Node 生态衍生出的 Node 包管理器百花齐放,先后出现了 npm、kpm、pnpm、yarn、cnpm 等等。实际上 Node 包管理器发展主要分5个阶段,下面我们一起看下每个阶段的关键特征和代表产物吧~
正确来说,Node是不存在没有包管理器时期的,2009年,Node.js 问世时 npm的雏形也发布了。【相关教程推荐:nodejs视频教程、编程教学】
npm 全称 Node.js Package Manager;从A brief history of Node.js 里面可以看到
2009 Node.js is born The first form of npm is created
下面聊一下没有出现Node包管理器时期是怎样的,那时候做的更多的事情就是
网上寻找各软件的官网,比如 jQuery;
找到下载地址,下载 zip 包;
解压,放到项目中一个叫 libs 的目录中;
想更方便的话,直接将 CDN 链接粘贴到 HTML 中
那时候 模块化管理?版本号管理?依赖升级?都不存在的!
2009 年,Node.js 诞生,npm的雏形也正在酝酿,2011年发布了 1.0 版本;npm是围绕着语义版本控制 semver 的思想而设计的,默认认为Node包开发者,在升级依赖包自定义版本号时,都会按照 semver 规范升级版本号。
代名词: Node包管理规范化、node_modules 目录嵌套存放依赖
代表产物: npm v1、v2 版本
关键特点:
(1)依赖包嵌套安装,相同版本的依赖会被冗余安装
(2)依赖包安装不确定性:默认装最新次版本的依赖包(可设置固定版本)
(3)串行安装依赖,速度慢;不支持离线缓存
解释1:依赖 嵌套安装 ,如果A依赖B、B依赖C,node_modules目录如下
node_modules - package-A -- node_modules --- package-B ----- node_modules ------ package-C -------- some-really-really-really-long-file-name-in-package-c.js
问题: 依赖嵌套过多会造成嵌套地狱,与此同时会出现大量相同依赖包的冗余安装,造成 node_modules 体积过大,需要程序员定期的 rm -rf node_modules,但windows系统,很多程序无法处理超过260个字符的文件路径名,早期 npm 的 windows 用户都见过这个弹窗
解释2:针对每次 npm install 默认安装最新次版本依赖,造成的依赖安装 不确定性 问题:
semver 规范了版本号构成:X.Y.Z-[state],版本号升级规范如下
版本不确定原因:执行 npm 默认安装依赖指令时,npm 认为开发者都会遵循 semver 版本升级规范,直接给开发者安装 了最新次版本的依赖包
解决方案2:npm提供了 shrinkwrap 命令,会生成一个 npm-shrinkwrap.json
文件,为所有库和所有嵌套依赖的库记录精准的版本
总结:锁文件不会默认生成,需要用户手动执行指令;依赖于用户知道这个指令,相对繁琐
2015年,为解决 npm1、npm2 存在的 嵌套安装、版本不规定问题,完全重写了 npm 程序
代名词: 较少冗余依赖的安装、node_modules 目录扁平化存放依赖
代表产物: npm v3版本
原理简述: npm install时,先构建依赖树再将所有依赖都安装在 node_modules 根目录,子依赖遇到不同版本的重名依赖时,会将子依赖安装在自己node_modules下
关键特点:
(1)减少冗余安装:依赖扁平化安装,一定情况下减少了冗余包的安装
存在的问题:
(1)“幽灵依赖”、“幻影依赖” 问题
(2)“双胞胎陌生人” 、“依赖包分身” 问题
(3)目录不固定:依赖的安装顺序,决定了 node_module 目录结构
解释1:依赖的依次安装顺序,决定了node_modules 目录结构
如下场景: App1 依赖 packageA 和 packageC 和 packageG 和 packageH,而 packageA 和 packageC 都依赖了 packageB v1.0,packageG、packageH 都依赖 packageB 的 v2.0 版本
如果先安装 packageA 或 packageC,node_modules 目录如下
如果先安装 packageG 或 packageH,node_modules 目录如下
针对如上情况,npm 提供了指令 npm dedupe
,可以手动整理&简化 node_modules 的目录结构,整理后的 node_modules 目录结构是一致的,不受依赖包安装顺序影响
解释2:不一定能减少冗余包的安装
通过举例1,可以看出虽然依赖包扁平化安装,但仍存在相同版本的依赖包,有冗余包
解释3:“双胞胎陌生人” 问题
参考举例1中,相同版本的依赖包,被安装两次,被放置到两处,这种现象被称为“双胞胎陌生人”
解释4:“幽灵依赖” 问题
node_modules 一级目录下的依赖包,开发者可以直接使用,但依赖包没定义在 package.json 中,这样的依赖包被称为 “幽灵依赖”,前端项目中使用 “幽灵依赖”,后期可能会出现问题。
因为后期可能伴随着 其他依赖的升级移除了这个“幽灵依赖”,此时 node_modules 中就不存在了,执行 npm install 时,并不会主观的去安装 “幽灵依赖”,导致项目找不到依赖包,然后报错。
2016年,yarn、pnpm release 版本相继出现,在一定程度上解决了之前的 安装版本不确定、安装速度慢等问题,yarn 率先推出的能力相比于 npm,更夺人眼球,保证了一致性&安全性,提升了安装速度
代名词: 依赖安装相对安全、提速
代表产物: yarn release版本、pnpm release版本、npm v5 版本(npm v4没有太大变化,v5向前迈了一大步)
关键特点:
(1)安全:默认生成版本锁文件,保证了每次安装依赖版本都一样
(2)提速:增加了缓存离线安装、并行安装、安装异常后自动重试
(3)workspace:yarn从v1版本开始支持,可以高效管理多个项目的依赖包;npm v7才支持 workspace
存在的问题:
(1)幽灵依赖
(2)依赖包单项目重复安装、跨项目重复安装
(3)目录不固定:依赖的安装顺序,决定了 node_module 目录结构
解释1:关于安全
yarn v0.x 版本率先、npm v5.x 版本紧跟其后,下载依赖时,默认生成 依赖锁文件,精确地将版本锁定在一个值
总结: 避免了不同终端安装依赖时版本不一致问题,但“依赖幽灵” 问题仍然存在
解释2:关于提速 - 离线缓存
npm v2版本 就支持了缓存,但需要联网检测,才能使用 缓存的依赖
yarn v0.x版本 率先支持了 离线缓存,从网络下载的包都会缓存到全局,下次安装优先从本地找,找到直接copy
npm v5版本 重写了缓存系统,也支持了离线安装,安装速度大大提升
解释3:关于提速 - 并行安装
yarn 率先支持了依赖包并行安装,此前 npm 串行安装依赖,后来 npm 也优化了并行安装
解释4:安装依赖后,自动整理 node_modules 目录
起始时间:yarn v1.x 版本、npm v4.x 版本
.lock
文件,初始化安装依赖时,会自动整理 node_modules 目录;npm v4.x之前版本,需要用户手动执行指令 npm dedupe
整理依赖目录对着 pnpm 的成熟,开发者们享受着 pnpm 带来的 更安全、更高速、存储更低耗 等红利,解决了“幽灵依赖” 问题,解决了重复依赖问题
代名词: 安全(依赖安装所见即所得)、高速(不重复安装)、存储低耗(硬链接 + 软链接)
代表产物: pnpm
关键特点:
(1)速度极快:存储中心集中管理依赖,直接硬链接到项目的 node_modules/.pnpm
中。相比于之前的工作方式,减少了从全局 node_modules copy 到项目内的大量 IO 操作
(2)磁盘利用率极高:
(3)安全性高:node_modules 目录结构就是 package.json 依赖列表,解决了 “幽灵依赖” 问题
性能表现如下:
更多node相关知识,请访问:nodejs 教程!
以上是一文聊聊Node包管理发展的五个阶段的详细内容。更多信息请关注PHP中文网其他相关文章!