[译] 理解 NPM 5 中的 lock 文件

理解 NPM 5 中的 lock 文件

NPM 的下个主版本(NPM 5)在速度、安全性和一堆其余时髦的东西上,相比较前一个版本带来了一些改进。然而从用户的角度来看,最突出的就是全新的 lock 文件,不止一个 lock 文件。咱们一下子再谈论这个。对于新手来讲,一个 package.json 文件使用了语义化版本规范,去描述对于其余包的直接依赖,而这些包可能依赖于其余包等等,以此类推。lock 文件则是整个依赖关系树的快照,包含了全部包及其解析的版本。html

与以前版本相反,lock 文件如今包含一个 integrity 字段,它使用 Subresource Integrity 来验证已安装的软件包是否被改动过,换句话来讲,验证包是否已失效。它依旧支持旧版本 NPM 中对包的加密算法 SHA-1,可是之后将默认使用 SHA-512 进行加密。前端

这个文件目前取消from 字段。众所周知,这个字段和时常发生不一致的 version 字段一块儿,给代码审查看文件改动差别时,带来了很多痛苦。不过如今应该变得更加整洁了。node

该文件如今增长了 lockfileVersion 字段来指定的 lock 格式的版本,并将其设置为1。这是为了使未来的格式更新时,不用去猜想该文件使用什么特定版本。之前的 lock 格式仍然支持并被识别为版本 0react

{
  "name": "package-name",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "dependencies": {
    "cacache": {
      "version": "9.2.6",
      "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz",
      "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg=="
    },
    "duplexify": {
      "version": "3.5.0",
      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
      "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
      "dependencies": {
        "end-of-stream": {
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
          "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4="
        },复制代码

你可能已经注意到了,指向特定 URI 的文件的 resolved 字段仍然获得了保留。注意,NPM 如今能够(根据 .npmrc 中的设置)解析机器配置使用的不一样仓库,这样的话,与 integrity 字段一块儿配合,只要签名是匹配的,包的来源并没有关紧要。android

值得一提的是,lock 文件精确描述了 node_modules 目录中所列出的目录的物理树。其优势是,即便不一样的开发人员使用不一样版本的 NPM,他们仍然不只可以获得相同版本的依赖,还可使用彻底相同的目录树。 这与其余包管理器(如 Yarn )不一样。 Yarn 仅以 flatten 格式 描述各个包之间的依赖关系,并依赖于其当前实现来建立目录结构。这意味着若是其内部算法发生变化,结构也会发生变化。若是你想了解更多关于 Yarn 和 NPM 5 之间 lock 文件的区别,请查看 Yarn determinismios

双 lock 文件

上面已经提到过 lock 文件不止一个。当安装新的依赖关系或文件不存在时,NPM 将自动生成一个名为 package-lock.json 的 lock 文件。如开始所述,lock 文件是当前依赖关系树的快照,容许不一样机器间的重复构建。所以,建议将它添加到您的版本控制中去。git

你可能会认为,使用 npm shrinkwrap 及其 npm-shrinkwrap.json 能够实现一样的效果。你的想法没错,但建立新 lock 文件的缘由是,这样可以更好的传达一个信息,就是 NPM 真正支持了 locking 机制,这在之前确实是一个显著的问题。github

不过仍是有一些区别。首先,NPM 强制该 package-lock.json 不会被发布。 即便你将其显式添加到软件包的 files 属性中,它也不会是已发布软件包的一部分。这种状况一样不适用于 npm-shrinkwrap.json 文件,哪怕这个文件能够是发布包的一部分、即使存在嵌套的依赖关系,NPM 也会遵照它。你能够简单的经过运行 npm pack 来查看生成的归档内部的内容。web

接下来,您可能会想知道在已经包含 package-lock.json 的目录中运行 npm shrinkwrap 时会发生什么。答案很简单,NPM 仅仅会把 package-lock.json 重命名为 npm-shrinkwrap.json。由于文件的格式是彻底同样的。算法

最好奇的还会问,当两个文件都存在时会发生什么。 在这种状况下,NPM将彻底忽略 package-lock.json,只使用 npm-shrinkwrap.json。 当只使用 NPM 操纵文件时,这种状况不该该发生。

总结:

  • NPM 会在安装包时自动建立 package-lock.json,除非已经有 npm-shrinkwrap.json,并在必要时更新它。

  • 新的 package-lock.json 永远不会被发布,并且应该将其添加到你的版本控制系统中去。

  • 运行已经带有 package-lock.json 文件的 npm shrinkwrap 命令将只会对其重命名为 npm-shrinkwrap.json

  • 当两个文件处于某些缘由同时存在时,package-lock.json 将被忽略。

这很酷,可是何时使用新的 lock 文件而不是旧的 shrinkwrap? 它一般取决于您正在处理的包的类型。

当开发库时

若是你正在开发一个库(如其余人所依赖的软件包),则应使用新的 lock 文件。 另外一种替代方案是使用 shrinkwrap,并确保它不会随包发布(新的 lock 文件不会自动发布)。 但为何不发布 shrinkwrap 呢? 这是由于 NPM 遵照在包中找到的 shrinkwraps,而且因为 shrinkwrap 老是指向单个包的特定版本,因此你没法利用 NPM 可使用相同的包来知足多个包的要求(在 semver 容许范围内)的优点。 换句话说,经过不去强制 NPM 来安装特定的版本,您可让 NPM 更好的复用包,并使结果更小更快地组合。

这里有一个警告。当你正在开发库时,由于仓库中存在 package-lock.jsonnpm-shrinkwrap.json,因此每次都会得到彻底相同的依赖关系,这对于你的持续集成服务器也是如此。如今想象你的 package.json 指定某个包的依赖关系为 ^1.0.0,也刚好是 lock 文件中指定的版本,而且每次安装。到目前为止一切正常。但若是依赖项发布了一个新版本,而且意外的破坏了 semver 和你开发的包,这时候会发生什么?

遗憾的是,在出现错误报告以前,你可能没法注意到这个问题。在没有 lock 文件的仓库中,你的构建至少在 CI 服务器上会失败,由于它老是尝试去安装依赖的 latest 版本,从而运行出错的版本(只要该版本按期运行,而不只仅是针对 PR)。 然而,当 lock 文件出现后,它将始终安装能正常工做的被 lock 的版本。

然而,对于这个问题有几个其余的解决方案。 首先,你能够牺牲问题重现的精确性,而将 lock 文件添加到版本控制系统中。 其次,你能够作一个分离的配置来进行构建,在运行测试以前运行 npm update。 第三,你能够简单的在你运行测试以前删除 lock。 如何处理发现的损坏依赖是另外一个话题了,其主要缘由是由于 NPM 实现的 semver 不只没有涉及如此广范围的问题,并且还不支持特定版本的黑名单特性。

这固然就会引发一个问题,在开发库的时候,是否真的值得将 lock 文件添加到版本控制中去。要记住的是,lock 文件不只包含依赖关系,还包含 dev 的依赖关系。在这种意义下来说,开发库与开发应用时相似(见下一节),不管何时都有着彻底相同的 dev 依赖关系,而且不一样设备也算一种优点。

当开发应用时

好,那么最终用户在终端中使用的包或打包的可执行文件会是个什么状况?在这种状况下,包就是最终结果,即应用。你想要确保最终用户总能得到你发布时所具备的确切依赖性。确保在安装时让 NPM 遵照规则,这就是您想要使用 shrinkwrap 的地方。 记住,使用 npm pack 发布包时,你能够随时查看软件包的状况。

注意,在 package.json 中指定一个特定版本依赖是不够的,由于你但愿确保最终用户得到彻底相同的依赖关系树,包括其全部子依赖关系。而 package.json 中的一个特定版本保证只会发生在顶层。

其余类型的应用怎么样,好比在仓库内启动的项目?这种状况并不重要。重要的是安装正确的依赖项,而两个 lock 都知足这一点要求。随你怎么选。

结束

没了,就这么多。若是有哪里不对或者有一些通常性的意见,请随时在 Twitter 上联系我。若是你发现拼写错误或语法问题,则能够在 GitHub 上找到这个文章。感谢你的帮助!

若是你喜欢这篇文章,你能够在 Twitter 上关注 @JiriPospisil 并经过 feed 订阅。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

相关文章
相关标签/搜索