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

本文讲的是[译] 理解 NPM 5 中的 lock 文件,

理解 NPM 5 中的 lock 文件

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

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

这个文件目前取消了 from 字段。众所周知,这个字段和时常发生不一致的 version 字段一起,给代码审查看文件改动差异时,带来了不少痛苦。不过现在应该变得更加整洁了。

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

{
  "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 字段一起配合,只要签名是匹配的,包的来源并无关紧要。

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

双 lock 文件

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

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

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

接下来,您可能会想知道在已经包含 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.json 或 npm-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 上找到这个文章。感谢你的帮助!






原文发布时间为:2017年6月16日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2024-12-28 17:36:45

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

[译] 理解 NodeJS 中基于事件驱动的架构

本文讲的是[译] 理解 NodeJS 中基于事件驱动的架构, 原文地址:Understanding Node.js Event-Driven Architecture 原文作者:Samer Buna 译文出自:掘金翻译计划 译者:刘德元 薛定谔的猫 校对者:bambooom zaraguo 理解 NodeJS 中基于事件驱动的架构 绝大部分 Node.js 对象,比如 HTTP 请求.响应以及"流",都使用了 eventEmitter 模块来支持监听和触发事件. 事件驱动最简单的形式是

c#-C#中Task中使用lock(this)锁定文件无效?是不是要file.close()?

问题描述 C#中Task中使用lock(this)锁定文件无效?是不是要file.close()? C#中Task中使用lock(this)锁定文件无效?是不是要file.close()? 解决方案 具体代码怎么写的,file是什么类型?还有lock(this)是不正确的写法http://blog.csdn.net/alisa525/article/details/7243029 解决方案二: C#使用Create创建文件后,报The Process cannot access the file

[译] 在大型应用中使用 Redux 的五个技巧

本文讲的是[译] 在大型应用中使用 Redux 的五个技巧, 原文地址:Five Tips for Working with Redux in Large Applications 原文作者:AppNexus Engineering 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:loveky 校对者:stormrabbit 在大型应用中使用 Redux 的五个技巧 Redux 是一个很棒的用于管理应用程序"状态(state)"的工具.单向数

【译】探索 Kotlin 中的隐性成本(第一部分)

本文讲的是[译]探索 Kotlin 中的隐性成本(第一部分), 原文地址:Exploring Kotlin's hidden costs - Part 1 原文作者:Christophe B. 译文出自:掘金翻译计划 译文地址:github.com/xitu/gold-m- 译者:Feximin 校对者:CACppuccino .phxnirvana Lambda 表达式和伴生对象 2016年,Jake Wharton 做了一系列有趣的关于 Java 的隐性成本 的讨论.差不多同一时期他开始提倡

在Mozilla UI中优化CSS文件的规则

原文地址:Writing Efficient CSS for use in the Mozilla UI 以下文档描述了应用在 Mozilla UI 中优化 CSS 文件的规则.第一部分是对于 Mozilla 样式系统分类规则的一般性讨论.在了解这个系统的基础上,后续部分包含了一些指南,书写可以利用这个样式系统实践优点的样式的指南. 样式系统如何分类规则 样式系统把规则分为四大类.理解这些类是很重要的,因为对于规则的匹配来说他们是首先要考虑的.之后的段落中会使用"主选择符"这个说法.主

使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践

提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OSS(https://www.aliyun.com/product/oss) 4.HTTPS(http://baike.baidu.com/view/14121.htm) 阅读目录结构 引: 一.准备工作 二.整体功能结构 三.具体实现步骤 四.关键点和问题处理 五.延伸与扩展 六.总结与思考 引:

详解Java中的File文件类以及FileDescriptor文件描述类_java

File File 是"文件"和"目录路径名"的抽象表示形式. File 直接继承于Object,实现了Serializable接口和Comparable接口.实现Serializable接口,意味着File对象支持序列化操作.而实现Comparable接口,意味着File对象之间可以比较大小:File能直接被存储在有序集合(如TreeSet.TreeMap中).1. 新建目录的常用方法方法1:根据相对路径新建目录. 示例代码如下(在当前路径下新建目录"d

[译] 理解 Service Workers

本文讲的是[译] 理解 Service Workers, 原文地址:Learning React.js is easier than you think 原文作者:Samer Buna 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:Cherry 校对者:LeviDing.undead25 学习 React.js 比你想象的要简单 通过 Medium 中的一篇文章来学习 React.js 的基本原理 你有没有注意到在 React 的 logo 中隐藏着

c语言-c中关于引用传递的举例还是没看懂怎么理解,形参中的&还能按取地址符号来用吗

问题描述 c中关于引用传递的举例还是没看懂怎么理解,形参中的&还能按取地址符号来用吗 解决方案 当作取地址的时候,&是一个一元运算符,和! ~ * -等类似.作为按引用传递的时候,&不是运算符,是修饰符. 函数定义中不能出现运算符. 不信你试试 int foo(int -a) int foo(int !a) 都是不能编译的. 解决方案二: 引用是引用,取地址是取地址.不同上下文,一个符号有不同的用途,不要瞎联系. 解决方案三: &还可以是按位and运算符呢.*还可以是乘法运