专栏首页马铖的专栏npm5 新版功能特性解析及与 yarn 评测对比
原创

npm5 新版功能特性解析及与 yarn 评测对比

前言

前段时间 npm 发布了 5.0 版本,提供了自动记录依赖树,下载使用强校验,重写缓存系统等功能升级和改造,吸引了不少关注。本文将对 npm5 的新功能和变化点在进行实践使用后进行介绍和总结,并和 yarn 进行简单对比。

更新一览

通过官方的 Release note 我们可以看到 npm5 的主要新功能和大改动主要有下面几点(后面将会详细介绍):

  1. 默认新增 package-lock.json 来记录依赖树信息,进行依赖锁定,并使用新的 shrinkwrap 格式。
  2. --save 变成了默认参数,执行 install 依赖包时默认都会带上,除非加上 --no-save。
  3. Git 依赖优化:支持指定 semver 版本安装;含有 prepare 脚本时将安装其 devDependencies 并执行脚本。
  4. 使用本地目录文件作为 file 类型依赖安装时,使用创建 symlink 的方式替代原来的文件拷贝方式,提升速度。
  5. 脚本更改:在 npm pack, npm publish 时新增 prepack 和 postpack 脚本;preinstall 脚本运行优先级提升到最前,并且可以修改 node_modules。
  6. 包发布将同时生成 sha512 和 sha1 校验码,下载依赖时将使用强校验算法。
  7. 重写整个缓存系统和 npm cache 系列命令。废除 --cache-min --cache-max 等命令,缓存系统将由 npm 自身维护,无需用户介入。
  8. registry 策略调整:配置优先级高于锁文件中记录的优先级;除非使用不同 scope 的包,不再支持不同的包使用不同的 registry。

除此之外还包含一些细节优化:

  1. 离线安装时将不再尝试连接网络,而是降级尝试从缓存中读取,或直接失败。
  2. 锁文件(package-lock.json, npm-shrinkwrap.json)将包含 optionalDependencies。
  3. 本地包(local tarball)具有 .tar, .tar.gz, 或 .tgz 后缀时才会被安装。
  4. 新增 notice 为默认 loglevel。
  5. node-gyp 在 Windows 提供对 node-gyp.cmd 的支持。
  6. 移除 ./cli.js,使用 ./bin/npm-cli.js 代替。

下面就来对主要的修改点做详细介绍和对比:

特性一:锁文件(lockfile)

package-lock.json

本次 npm5 新增了 package-lock.json 文件,在操作依赖时默认生成,用于记录和锁定依赖树的信息。使用过 yarn 的同学应该能感觉到,这次 npm5 的很多改动都有参考 yarn,这里估计也是在 yarn 的 lockfile 大受欢迎的背景下做出了这个修改(其实之前的 npm 版本并不是没有 lockfile,后面会提到)。

在首次执行 npm install 后,会默认创建 package-lock.json 文件。

之后再次 install 将根据此文件中的记录进行安装。

如果对依赖进行了修改(新增依赖、npm update 或 npm install 指定不同版本的包),都将更新记录到此文件中。

npm-shrinkwrap.json

npm5 新增的 package-lock.json 文件和之前通过 npm shrinkwrap 命令生成的 npm-shrinkwrap.json 文件的格式完全相同,文件内记录了版本,来源,树结构等所有依赖的 metadata。需要注意的是 npm shrinkwrap 并不是一个新功能特性,而是从 npm2 就开始有的功能。也就是说在 npm5 之前的版本也是可以通过 shrinkwrap 锁定依赖的。(在这一点上,其实 Facebook 也是早期在使用 npm shrinkwrap 等功能时无法满足需求才导致了现在 yarn 的出现。可以阅读 Facebook 的这篇文章了解他们开发 yarn 的动机。)

而最新的 npm5 在生成了 package-lock.json 之后,再运行 npm shrinkwrap 命令,会发现就是把 package-lock.json 重命名为 npm-shrinkwrap.json 而已。

因此 package-lock.json 表面上看只是把 npm-shrinkwrap.json 作为了默认创建,为何还要新建一个文件呢?官方对于此也给出了答复和解释:新增 package-lock.json 主要是为了使得 npm-shrinkwrap.json 可以向下兼容,保证旧版也可使用(比如已有 shrinkwrap 文件的项目,或是回滚旧版的场景)。另外 package-lock 的名称也比 shrinkwrap 相对更加直观。

具体可以查看 npm 核心开发者在 reddit 上的这篇解释

除此之外,package-lock.json 和 npm-shrinkwrap.json 在使用场景上也有以下不同点:

  • package-lock.json 用于开发人员锁定版本使用,应该提交到版本控制,不应该跟随发布。其只在项目顶级有效,放在依赖包中时此文件无效。
  • npm-shrinkwrap.json 可以作为库的依赖锁进行发布。当依赖包有此文件时,将按照此文件安装其下游依赖。
  • 当两个文件同时存在时,npm-shrinkwrap.json 有高优先级,package-lock.json 文件将被忽略。
  • 已有 npm-shrinkwrap.json 文件时,不会再创建 package-lock.json 文件。

具体规范文档可以参考:

过渡升级

关于 npm-shrinkwrap.json 文件,还要注意之前的版本是不包括 devDependencies 的,而这次升级 npm5 后 devDependencies 将被包括进来。所以如果使用旧版时已有 npm-shrinkwrap.json 文件,这次升级后再次 install 时会把这些缺少的依赖加进去(npm5 之后会通过 "lockfileVersion" 字段来判断 shrinkwrap 的版本)。因此多人开发时要注意大家同步升级,避免产生新旧混用的情况。

适用场景

综上我们能总结出 package-lock.json 和 npm-shrinkwrap.json 在项目中的适用场景:

  • 开发时提交和使用 package-lock.json 来保证不同环境、人员安装依赖的一致性。
  • 发布包时如果有锁定的需求,可以用 npm shrinkwrap 命令把 package-lock.json 转为 npm-shrinkwrap.json 随包发布。
  • 如果项目中已经在使用 npm-shrinkwrap.json,可以继续使用(但要注意从旧版本升级到 npm5 后 install 时会被更新),其优先级高于 package-lock.json,并且不会再被重复创建。

和 yarn 的差异

手动修改 package.json 依赖版本:

我们已经知道,生成 package-lock.json 后,重复执行 npm install 时将会以其记录的版本来安装。这时如果手动修改 package.json 中的版本,重新安装也不会生效,只能手动执行 npm install 命令指定依赖版本来进行修改。

而这一点 yarn 是可以做到的。猜想 yarn 在执行前是先对比了一遍 package.json 和 yarn.lock 中的版本,如果版本范围完全不符的话会重新安装并更新 lockfile。

registry 优先级:

在 npm5 中,配置的 registry 优先级要高于 lockfile 内记录的 registry 的优先级。如果设置的 registry 和 lockfile 中的不同,将使用设置的 registry 安装(命中缓存时除外)。

这一点也和 yarn 不同。yarn 在有 lockfile 时会完全忽略 config 中的 registry。个人感觉 npm5 的设定更加合理,否则有频繁切换 registry 需求的用户可能会比较难受。

特性二:Git 依赖支持优化

新版本对 Git 依赖支持了通过 semver 版本号安装指定的版本。例如我可以通过以下命令安装 Github 上的 chalk 1.0.0 版本:

npm install git+https://github.com/chalk/chalk.git#semver:1.0.0

这个特性在需要安装大量内部项目(例如在没有自建源的内网开发),或需要使用某些依赖的未发布版本时很有用。在这之前可能需要使用指定 commit_id 的方式来控制版本。

除此之外,这次升级还对 Git 依赖增加了 prepare 脚本的支持。声明 perpare 脚本时,依赖被安装时将会同时安装它的 devDependencies,然后执行脚本。详细可以看开发人员在 twitter 上的这个演示

特性三:文件依赖优化

在之前的版本,如果将本地目录作为依赖来安装,将会把文件目录作为副本拷贝到 node_modules 中。而在 npm5 中,将改为使用创建 symlinks 的方式来实现(使用本地 tarball 包除外),而不再执行文件拷贝。这将会提升安装速度:

npm install ../packages/mylib
npm install file://packages/mylib

使用文件安装依赖这个功能仅有 npm 支持,目前 yarn 还不支持(截至 yarn@0.24.6)。

有关新的 file:// 规范描述可以参考官方的 file-specifiers

具体修改和实现可以参考这个 PR

特性四:缓存优化

新版本重写了整个缓存系统,缓存将由 npm 来全局维护不用用户操心,这点也是在向 yarn 看齐。升级新版后,用户基本没有手动操作 npm cache 的场景。npm cache clean 将必须带上 --force 参数才能执行,并且会收到警告:

--cache-min --cache-max 也已废除。同时在离线安装等场景将会提高缓存使用的优先级,提升安装速度。

速度对比

这里使用目前最新的 npm@5.0.3 版本,和 yarn@0.24.6 版本,分别在使用各自的官方 registry 和 taobao registry 的情况下进行以下几种场景的速度对比:

  1. 首次安装
  2. 有缓存,有 lockfile 情况重复安装
  3. 有缓存,无 lockfile 情况重复安装
  4. 无缓存,有 lockfile 情况重复安装
  5. 无缓存,无 lockfile 情况重复安装
  6. 删除 node_modules,有缓存,有 lockfile 情况安装
  7. 删除 node_modules,有缓存,无 lockfile 情况安装
  8. 删除 node_modules,无缓存,有 lockfile 情况安装

registry:

用于测试的 package.json:

{
  "name": "test1",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^15.5.4",
    "react-dom": "^15.5.4",
    "react-native": "^0.45.1",
    "react-redux": "^5.0.5",
    "redux": "^3.6.0"
  },
  "devDependencies": {
    "babel-core": "^6.25.0",
    "babel-loader": "^7.0.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "webpack": "^2.6.1"
  }
}

测试机型:

MacBook Pro (15-inch, 2016)

macOS Sierra 10.12.3

CPU2.9 GHz Intel Core i7

网络(无GFW):

评测结果:

|场景| npm5 |npm5(taobao registry)|yarn |yarn(taobao registry)|

|---------|---------|---------|

|首次安装 |45.856s| 40.442s |33.26s |40.85s|

|有缓存 有 lockfile |3.527s| 2.153s| 0.69s |0.57s|

|有缓存 无 lockfile |2.79s |2.405s |10.04s| 15.59s|

|无缓存 有 lockfile |2.465s |2.179s |0.58s |0.56s|

|无缓存 无 lockfile| 2.785s| 2.511s |27.10s |37.86s|

|有缓存 有 lockfile 删掉 node_modules|24.824s| 16.228s| 14.31s| 14.11s|

|有缓存 无 lockfile 删掉 node_modules|44.764s| 30.035s |25.41s |27.91s|

|无缓存 有 lockfile 删掉 node_modules|28.479s |32.827s |25.54s |29.80s|

通过对比可以看出,yarn 的速度在大部分正常场景下还是略高一筹,不过相比之下 npm5 的差距已经很小。

单从数据来看最佳的搭配组合竟然为 yarn 和官方 registry,甚至更换 taobao registry 之后速度反而有所下降,目前还不知道 yarn 是否在自己的源上也有做了特殊优化。

注:单机测试样本不足,也可能有波动或误差,数据仅供参考,感兴趣的读者也可以用自己的项目和网络测一下。

详细测试过程可以查看视频:

视频内容
视频内容

总结

通过以上一系列对比,我们可以看到 npm5 在速度和使用上确实有了很大提升,值得尝试。但从速度上来说 yarn 貌似还是更快一点,也没有足够的理由停止使用 yarn。

综上我个人的建议是如果你已经在个人项目上使用 yarn,并且没有遇到更多问题,目前完全可以继续使用。但如果有兼容 npm 的场景,或者身处在使用 npm,cnpm,tnpm 的团队,以及还没有切到 yarn 的项目,那现在就可以试一试 npm5 了。(另外如果对稳定性要求比较高,鉴于现在才刚发布不久,也可以先了解一下当前的 BUG List 进行衡量)。

以上内容均为原创,如有缪误请指出!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 解决oracle服务占用内存过高的问题

    通常我们在自己电脑上搭建项目环境时,都免不了要安装Oracle。不管你硬件多强悍,都会发现,Oracle服务一旦启用,内存立马吃紧。笔者内存8G,启动一个VS,...

    浩Coding
  • Dimple在左耳听风ARTS打卡(十九)

    所谓ARTS:每周至少做一个LeetCode的算法题;阅读并点评至少一篇英文技术文章;学习至少一个技术技巧;分享一篇有观点和思考的技术文章。(也就是Algori...

    程序员小跃
  • 设计模式常见面试知识点总结

    这篇总结主要是基于我设计模式系列的文章而形成的的。主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点。谢谢

    黄小斜
  • 一文了解设计模式的常见面试知识点

    这篇总结主要是基于我设计模式系列的文章而形成的的。主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点。谢谢

    Java技术江湖
  • 设计模式常见面试知识点总结

    这篇总结主要是基于我设计模式系列的文章而形成的的。主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点。谢谢

    黄泽杰
  • springboot + vue 跨域处理

    在使用 vue 做前端开发时,碰到 vue 请求接口出现跨域问题。 解决的方法,就在后台添加一个跨域请求的过滤器,来添加跨域支持。

    潇洒
  • 解决ajax跨域问题的一种方法

    前后端分离经常用json来传输数据,比较常见的问题就有ajax跨域请求的错误问题,这里是我的一种解决方法:

    Dream城堡
  • 修复shiro重定向引起的Response for preflight is invalid (redirect)的网络报错问题

    最近集成shiro到项目中,遇到该一个报复Response for preflight is invalid (redirect)的问题。

    星痕
  • Python基础 | 你想要的随机数生成都在这里

    在利用Python在进行数据分析的时候,经常需要按照某种规则快速生成实数序列,尤其是在学习matplotlib绘图的时候,需要模拟生成数据,然后开始绘制。

    算法与编程之美
  • 将移动硬盘上的archlinux复制到笔记本电脑硬盘并引导

    将移动硬盘上的archlinux复制到笔记本电脑硬盘并引导 每次安装archlinux我都非常痛苦,在同事的协助下才能完成。为了避免这个问题,我在我的移动硬盘上...

    FungLeo

扫码关注云+社区

领取腾讯云代金券