searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

前端常用包管理工具及其特点

2023-06-02 06:16:10
8
0

目前前端常用的包管理工具有如下几个,对于常用包管理工具,其管理的包都需要遵循semver规范

npm

cnpm

yarn

pnpm

 

Semver规范

举例而言

1.5.4 是版本号,其中

  • 1 是主版本号,代表的是不兼容的 API 修改
  • 5 是次版本号,代表的是向下兼容的功能性新增
  • 4 是修订号,代表的是向下兼容的问题修正
  •  

package.json 中的更新规范

  • 如果写入的是 〜0.13.0,则只更新补丁版本:即 0.13.1 可以,但 0.14.0 不可以。
  • 如果写入的是 ^0.13.0,则要更新补丁版本和次版本:即 0.13.1、0.14.0、依此类推。
  • 如果写入的是 0.13.0,则始终使用确切的版本。

 

 

npm

通过 npm install 进行安装,一般的npm 包都遵循Semver 规范

  1. 将依赖包的版本解析为某个具体的版本号
  2. 下载对应版本依赖的tar 包到本地离线镜像
  3. 将依赖从离线镜像解压到本地缓存
  4. 将依赖从缓存拷贝到当前目录的 node_nodules目录

 

目前存在的问题

对于 npm@3 之后,避免了嵌套而使用扁平化的包管理方式,但同时存在以下问题

  • 依赖结构的不确定性和二重身(doppelgangers)

    一个包的不同版本还是会重复安装(不能打平同一个包的不同版本),能会造成同一个包重复安装,性能还是会损失

  • 项目中仍然可以**非法访问依赖 (phatom)**没有声明过依赖的包

    package.json只声明了 A, 但 AdepdenciesB, 这样安装在 AB 也会被安装,项目中还是可以 requireB,这是正是由于扁平化引起的

  • 扁平化算法本身的复杂性很高,耗时较长。

对于依赖结构不确定的情况,比如项目中存在如下两个包 foobar,依赖关系如下

  • foo 依赖 base64-js@1.0.1
  • bar 依赖 base64-js@1.0.2

则扁平化后可能是下面两种中的一种(依赖于foobarpackage.json 中的位置

一部分为了解决如上的问题,在npm@5 版本后,推出了package-lock.json ,其会固化当前每个软件包的版本,而在下次的npm install 中,会使用这些确切的版本,同时其也会锁定其依赖的依赖。

如果想要更新 package-lock.json 中的版本,可以执行 npm update ,此命令会根据 Semver 规范更新依赖包,但是出于谨慎,最好还是单次升级某个特定的包:npm update xxpackage

其他常用命令

npm list --depth=0 查看整个项目的当前安装的依赖包版本。

npm outdated 查询已安装的包离最新版本的距离,Latest是这个包的最新大版本,Wanted 是这个包当前大版本下的最新小版本。

 

yarn

npm@5 之前因为没有锁定版本的功能,所以有人使用了 yarnyarn.lock 来补足,npm@5 以后在这点上差异不大,同时, 因为yarn 是并行安装(PnP),所以安装速度上会更快一些,其他地方差异不大。

这里注意下,对于yarn ,和npm 不同的是,需要同时拥有 yarn.lockpackage.json 才能保证其依赖包的确定性。

但对于扁平化算法复杂package非法访问 的问题依然存在

 

cnpm

淘宝产出的国内的包管理器,由于其镜像在淘宝机房,因此国内的安装速度比较好,但是有部分包内依赖包还是调用了npm i ,所以还是可能会慢。

缺点

**cnpm没有package-lock.json**

这个问题不用说了,可能导致多个环境对应的依赖版本不一致,上线可能导致各种各样的问题

cnpm使用软链接

cnpm 进行一次安装后,是软链接(符号链接)到了对应的npm包上(类似一个有垃圾回收机制的指针),而如果此时又进行了npm install 的操作,则新的npm包可能更新,这会使得cnpm对应的包实际也变成了新的,那么在对应的cnpm再次引入的时候就可能导致错误

可以通过如下命令来使用原有的npm的包管理方式而不适用软链接,但是会损失速度

cnpm i --by=npm
cnpm i --by=npm react-native

 

pnpm

pnpm的安装速度较快,同时其基于内容寻址的文件系统来存储文件,他会在全局的 store 目录下存储 node_modules 文件的 hardlink,这样,用户可以通过不同路径去寻找某个文件。

  • 对于同一个包,不会重复安装,而是只安装一份,之后的使用都是直接通过硬链接

  • 对于同一个包的不同版本,会尽可能复用之前版本的代码,比如更新后多了一个文件,则是生成之前文件的硬链接,同时写入那一个新文件

  • 什么是 hardlinksymlink(symbolic link)

    hardlink:有垃圾回收的指针

    如果A是B的硬链接,则AindexNodeBindexNode 指向同一个,删除其中任何一个都不会影响另外一个,只有当所有指向同一文件的硬链接删除后,该文件才真正删除。

    symlink : 没有垃圾回收的指针

    也可类比为桌面的快捷方式,比如A是B的软链接,则A存放着B的路径,访问A时会自动找到B,如果B删除,而A存在,则A指向了一个无效链接

一般pnpmnode_modules文件的hardlink会放到全局的store目录下(nvm下的安装除外)

${os.homedir}/.pnpm-store/v3/files

store 目录也会随着安装包的数量增大,使用 pnpm store prune 可以删除不再被引用的包

 

pnpm的树形 + 扁平结构

我们安装一个swiper 的包,此时会自动生成一个pnpm-lock.yaml文件,用来记录当前安装的这个包和这个包依赖的所有包

安装任意一个包后,在项目的根 node_modules 目录下会存在两个目录,一个是.pnpm 虚拟磁盘目录,用户不能直接从这个目录中require 另一个是真正这个包的目录,也就是 jsrequire 的路径,这个目录当前只是一个软链接,软链接到.pnpm中的对应包的目录,这个目录就是一个硬链接了,而 .pnpm 中的这个硬链接,会真实链接到全局的 store

 

phatom (非法访问依赖)的解决

由于依赖打平是在 .pnpm 目录中的,而不是在 node_modules 中, 无法直接被项目require ,也就不存在非法访问了

doppelgangers (二重身)的解决

因为pnpm的依赖始终是安装在全局的store下的,所以不存在安装重复的包的情况,一个包的不同版本始终只会被安装一次

 

独有特性

可以和.npmrc文件联动,从.npmrc 读取指定的node版本,并以该版本执行 pnpm run 等命令

对于使用了nvm 来管理node版本的情况(nvm也是npm官方所推荐的node版本管理工具)

 

支持monorepo

通过全局配置 pnpm-workspace.yaml

packages:
  # all packages in subdirs of packages/ and components/
  - 'packages/**'

或者通过 pnpm 命令安装全局公共包

pnpm install react react-dom -w // 根目录
pnpm i dayjs -r --filter packageName  // 安装在指定的 packages下

 

 

0条评论
作者已关闭评论
陈****东
3文章数
0粉丝数
陈****东
3 文章 | 0 粉丝
陈****东
3文章数
0粉丝数
陈****东
3 文章 | 0 粉丝
原创

前端常用包管理工具及其特点

2023-06-02 06:16:10
8
0

目前前端常用的包管理工具有如下几个,对于常用包管理工具,其管理的包都需要遵循semver规范

npm

cnpm

yarn

pnpm

 

Semver规范

举例而言

1.5.4 是版本号,其中

  • 1 是主版本号,代表的是不兼容的 API 修改
  • 5 是次版本号,代表的是向下兼容的功能性新增
  • 4 是修订号,代表的是向下兼容的问题修正
  •  

package.json 中的更新规范

  • 如果写入的是 〜0.13.0,则只更新补丁版本:即 0.13.1 可以,但 0.14.0 不可以。
  • 如果写入的是 ^0.13.0,则要更新补丁版本和次版本:即 0.13.1、0.14.0、依此类推。
  • 如果写入的是 0.13.0,则始终使用确切的版本。

 

 

npm

通过 npm install 进行安装,一般的npm 包都遵循Semver 规范

  1. 将依赖包的版本解析为某个具体的版本号
  2. 下载对应版本依赖的tar 包到本地离线镜像
  3. 将依赖从离线镜像解压到本地缓存
  4. 将依赖从缓存拷贝到当前目录的 node_nodules目录

 

目前存在的问题

对于 npm@3 之后,避免了嵌套而使用扁平化的包管理方式,但同时存在以下问题

  • 依赖结构的不确定性和二重身(doppelgangers)

    一个包的不同版本还是会重复安装(不能打平同一个包的不同版本),能会造成同一个包重复安装,性能还是会损失

  • 项目中仍然可以**非法访问依赖 (phatom)**没有声明过依赖的包

    package.json只声明了 A, 但 AdepdenciesB, 这样安装在 AB 也会被安装,项目中还是可以 requireB,这是正是由于扁平化引起的

  • 扁平化算法本身的复杂性很高,耗时较长。

对于依赖结构不确定的情况,比如项目中存在如下两个包 foobar,依赖关系如下

  • foo 依赖 base64-js@1.0.1
  • bar 依赖 base64-js@1.0.2

则扁平化后可能是下面两种中的一种(依赖于foobarpackage.json 中的位置

一部分为了解决如上的问题,在npm@5 版本后,推出了package-lock.json ,其会固化当前每个软件包的版本,而在下次的npm install 中,会使用这些确切的版本,同时其也会锁定其依赖的依赖。

如果想要更新 package-lock.json 中的版本,可以执行 npm update ,此命令会根据 Semver 规范更新依赖包,但是出于谨慎,最好还是单次升级某个特定的包:npm update xxpackage

其他常用命令

npm list --depth=0 查看整个项目的当前安装的依赖包版本。

npm outdated 查询已安装的包离最新版本的距离,Latest是这个包的最新大版本,Wanted 是这个包当前大版本下的最新小版本。

 

yarn

npm@5 之前因为没有锁定版本的功能,所以有人使用了 yarnyarn.lock 来补足,npm@5 以后在这点上差异不大,同时, 因为yarn 是并行安装(PnP),所以安装速度上会更快一些,其他地方差异不大。

这里注意下,对于yarn ,和npm 不同的是,需要同时拥有 yarn.lockpackage.json 才能保证其依赖包的确定性。

但对于扁平化算法复杂package非法访问 的问题依然存在

 

cnpm

淘宝产出的国内的包管理器,由于其镜像在淘宝机房,因此国内的安装速度比较好,但是有部分包内依赖包还是调用了npm i ,所以还是可能会慢。

缺点

**cnpm没有package-lock.json**

这个问题不用说了,可能导致多个环境对应的依赖版本不一致,上线可能导致各种各样的问题

cnpm使用软链接

cnpm 进行一次安装后,是软链接(符号链接)到了对应的npm包上(类似一个有垃圾回收机制的指针),而如果此时又进行了npm install 的操作,则新的npm包可能更新,这会使得cnpm对应的包实际也变成了新的,那么在对应的cnpm再次引入的时候就可能导致错误

可以通过如下命令来使用原有的npm的包管理方式而不适用软链接,但是会损失速度

cnpm i --by=npm
cnpm i --by=npm react-native

 

pnpm

pnpm的安装速度较快,同时其基于内容寻址的文件系统来存储文件,他会在全局的 store 目录下存储 node_modules 文件的 hardlink,这样,用户可以通过不同路径去寻找某个文件。

  • 对于同一个包,不会重复安装,而是只安装一份,之后的使用都是直接通过硬链接

  • 对于同一个包的不同版本,会尽可能复用之前版本的代码,比如更新后多了一个文件,则是生成之前文件的硬链接,同时写入那一个新文件

  • 什么是 hardlinksymlink(symbolic link)

    hardlink:有垃圾回收的指针

    如果A是B的硬链接,则AindexNodeBindexNode 指向同一个,删除其中任何一个都不会影响另外一个,只有当所有指向同一文件的硬链接删除后,该文件才真正删除。

    symlink : 没有垃圾回收的指针

    也可类比为桌面的快捷方式,比如A是B的软链接,则A存放着B的路径,访问A时会自动找到B,如果B删除,而A存在,则A指向了一个无效链接

一般pnpmnode_modules文件的hardlink会放到全局的store目录下(nvm下的安装除外)

${os.homedir}/.pnpm-store/v3/files

store 目录也会随着安装包的数量增大,使用 pnpm store prune 可以删除不再被引用的包

 

pnpm的树形 + 扁平结构

我们安装一个swiper 的包,此时会自动生成一个pnpm-lock.yaml文件,用来记录当前安装的这个包和这个包依赖的所有包

安装任意一个包后,在项目的根 node_modules 目录下会存在两个目录,一个是.pnpm 虚拟磁盘目录,用户不能直接从这个目录中require 另一个是真正这个包的目录,也就是 jsrequire 的路径,这个目录当前只是一个软链接,软链接到.pnpm中的对应包的目录,这个目录就是一个硬链接了,而 .pnpm 中的这个硬链接,会真实链接到全局的 store

 

phatom (非法访问依赖)的解决

由于依赖打平是在 .pnpm 目录中的,而不是在 node_modules 中, 无法直接被项目require ,也就不存在非法访问了

doppelgangers (二重身)的解决

因为pnpm的依赖始终是安装在全局的store下的,所以不存在安装重复的包的情况,一个包的不同版本始终只会被安装一次

 

独有特性

可以和.npmrc文件联动,从.npmrc 读取指定的node版本,并以该版本执行 pnpm run 等命令

对于使用了nvm 来管理node版本的情况(nvm也是npm官方所推荐的node版本管理工具)

 

支持monorepo

通过全局配置 pnpm-workspace.yaml

packages:
  # all packages in subdirs of packages/ and components/
  - 'packages/**'

或者通过 pnpm 命令安装全局公共包

pnpm install react react-dom -w // 根目录
pnpm i dayjs -r --filter packageName  // 安装在指定的 packages下

 

 

文章来自个人专栏
js前端工程化
1 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0