什么时候我们不应该使用锁文件

调试程序时遇到的最常见情景之一可能是“在我的电脑上明明是好的”。这通常是由于对程序带有bug和自己系统的底层依赖不同的结果。 因此,yarn和npm在引入了的“lock files”,可以跟踪依赖项的确切版本。但是,当您开发将发布到npm的包时,应避免使用此类锁文件。在这篇博文中,我们将讨论为什么要这样。

快速总结

如果您构建一个类似web服务器的应用程序,那么锁定文件非常有用。但是,如果将库或CLI发布到npm,则永远不要发布锁文件。如果使用锁文件,则意味着您的用户和您可能使用不同版本的依赖项。

什么是锁文件?

一个锁文件描述了整个依赖树,因为它在创建时被解析,包括与特定版本的嵌套依赖关系。在npm中,这些被称为package-lock.json和在yarn中,它们被称为yarn.lock。在npm和yarn中,它们都放在package.json旁边。

package-lock.json看起来像这样:

{
 "name": "lockfile-demo",
 "version": "1.0.0",
 "lockfileVersion": 1,
 "requires": true,
 "dependencies": {
   "ansi-styles": {
     "version": "3.2.1",
     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
     "requires": {
       "color-convert": "^1.9.0"
     }
   },
   "chalk": {
     "version": "2.4.2",
     "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
     "requires": {
       "ansi-styles": "^3.2.1",
       "escape-string-regexp": "^1.0.5",
       "supports-color": "^5.3.0"
     }
   }
 }
}
复制代码

yarn.lock文件格式不同,但包含类似的信息:

# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


ansi-styles@^3.2.1:
  version "3.2.1"
  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
  dependencies:
        color-convert "^1.9.0"

chalk@^2.4.2:
  version "2.4.2"
  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
  dependencies:
        ansi-styles "^3.2.1"
        escape-string-regexp "^1.0.5"
        supports-color "^5.3.0"
复制代码

这两份文件都记录了一些重要的信息:

  1. 安装的每个依赖项的实际版本
  2. 每个依赖项的依赖项
  3. 已解析的包,包括一个校验和,用于验证包的完整性

因此,如果所有依赖项都列在锁文件中,为什么还要在package.json中列出它们呢?为什么我们需要两个文件?

package.json vs. Lock File

项目“package.json”文件中的dependencies字段的目的是显示应该安装的依赖项,而不是这些依赖项的依赖项。依赖项可以指定精确的版本或在Semver版本号范围内。对于使用semver版本号规则的,npm或yarn将选择最适合安装的版本。

这意味着,如果在发布新版本期间运行两次npm install,那么实际上可能得到不同依赖项的版本。例如,如果您使用npm install twilio安装一个叫twilio的依赖项。json可能有一个类似的条目:

{
  "dependencies": {
     "twilio": "^3.30.3"
  }
}
复制代码

如果你在npm页面上查看semver的文档,你会发现^实际上意味着任何大于3.30.3和小于4.0.0的版本都是有效的。因此,如果任何新版本发布,而你没有一个锁文件存在,npm或yarn将安装那个新的版本并不会自动更新package.json。然而,如果存在锁文件就不会这样子。

如果npm或yarn找到各自的锁文件,它们将使用这些文件进行模块安装。这对于在需要确保测试在可预测环境中运行的平台上进行持续集成(CI)之类的情况特别有用。对于这个用例,您可以使用特殊的命令或标志与相应的包管理器:

npm ci # 将确切地安装package-lock.json中的内容
yarn install --frozen-lock-file # 将准确地安装yarn.lock中的内容。不更新锁定
复制代码

这在构建web应用程序或服务器之类的应用程序时非常有用,因为在CI环境中,我们希望模拟用户的行为。因此,如果我们开始在源代码控制中跟踪锁文件(如git),我们可以确保每个开发人员、服务器、构建系统和CI系统使用相同版本的依赖。

那么,当我们编写库或其他打算发布到npm仓库的东西时,为什么不希望做同样的事情呢?为了回答这个问题,我们首先要谈谈发包是如何运作的。

如何发布模块

与一些人一般认知不同,发布到npm的内容并不总是与GitHub上的内容相同,也不总是与项目中的总体内容相同。模块发布的方式是,npm将通过检查package.json文件中的files配置和.npmignore文件来确定应该发布的文件。如果没有.npmignore文件则使用.gitignore文件。还有一些文件总是包含在其中,而另一些文件总是被排除在外。您可以在npm页面上找到这些文件的完整列表。例如,.git文件夹总是会被忽略。

之后,npm将获取文件列表,并使用npm pack将它们打包成一个tarball。如果你想查看哪些文件被打包,你可以运行npm pack --dry-run命令,它将输出与所有文件:

然后,该tarball将被上传到npm包仓库。运行此命令时,您可能注意到了,package-lock.json并没有被打入包中。这是因为package-lock.json总是会被忽略,正如npm文档中的列表所指定的那样。

这意味着如果其他开发人员安装了你发布的包,他们将永远不会下载你的package-lock.json。因此,在安装期间将完全不受锁文件影响。

这可能会意外地导致“在我的机器明明是好的”的效果,因为您的CI和开发人员环境可能会获得不同版本的依赖关系。那么我们应该做些什么呢?

禁用锁定文件和Shrinkwrapping文件

首先,我们应该确保停止跟踪锁定文件。如果您正在使用git,请将以下内容添加到项目中的.gitignore文件中:

yarn.lock
package-lock.json
复制代码

yarn官方文档说即使是你写的库,也应该登记一份yarn.lock文件。但是,如果你想确保拥有与用户相同的体验,我建议将yarn.lock添加到.gitignore

您可以关闭package-lock.json文件的生成。通过在项目中创建或将以下内容添加到.npmrc文件

package-lock = false
复制代码

对于yarn,您可以使用yarn install --no-lockfile命令不生成锁定文件。

然而,不是说因为我们不使用package-lock.json文件就没有办法锁定依赖项和子依赖项。我们还可以使用另一个名为npm-shrinkwrap.json的文件。

它基本上与package-lock.json相同。由npm shrinkwrap命令生成,并实际打包并发布到npm仓库。

因此,通过将npm shrinkwrap作为预打包脚本甚至git提交钩子添加到npm脚本中,您可以确保在开发环境、用户和CI中使用相同版本的依赖关系。

有一点很重要,一定要小心使用。通过使用shrinkwrap文件安装依赖包,你可以锁定准确的版本,这可能很棒,但它也可以阻止人们获得关键补丁。npm强烈反对对库使用shrinkwrap,并建议只在CLIs或类似的场景使用shrinkwrap

怎样才能得到更多信息

虽然在npm文档中有很多关于这方面的资料,但有时很难找到。如果您想更好地了解要安装或打包的内容,一个常见的命令就是--dry-run。运行该命令不影响应用。例如npm install --dry-run实际上不会将依赖项安装到项目目录,npm publish --dry-run实际上不会发布包。

下面是一些你可能想要查看的命令:

npm ci --dry-run # 基于package-lock.json 或者 npm-shrinkwrap.json文件模拟安装
npm pack --dry-run # 列出了所有要打包的文件以及元信息
npm install <dep> --verbose --dry-run # 将以verbose模式运行包的安装,而无需实际将其安装到文件系统
复制代码

有关该主题的一些有用的文档链接如下:

这在很大程度上取决于npm如何处理打包、发布和安装依赖项,而且随着不断变化的环境,这一点可能会在某一时刻发生变化。我希望这篇文章能让你对这个复杂的话题有更多的了解。

原文:When Not to Use Lock Files with Node.js

转载于:https://juejin.im/post/5cf61e315188251c06481987

猜你喜欢

转载自blog.csdn.net/weixin_34174105/article/details/91443977