什么时候不能在 Node.js 中使用 Lock Files
快速摘要
如果你开发像Web服务器之类的程序,那么lockfile是非常有用的。但是如果将库或CLI发布到npm,则永远不要发布lockfile。因为如果你使用它,则意味着你和你的用户可能在使用不同版本的依赖项。
什么是LockFile?
lockfile描述了整个依赖关系树,它在创建时被解析,包括具有特定版本的嵌套依赖关系。在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的格式不同,但也包含类似的信息:
#THISISANAUTOGENERATEDFILE.DONOTEDITTHISFILEDIRECTLY. #yarnlockfilev1 ansi-styles@^3.2.1: version"3.2.1" resolved"https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integritysha512-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" integritysha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles"^3.2.1" escape-string-regexp"^1.0.5" supports-color"^5.3.0"
两者都包含一些重要的信息:
- 安装的每个依赖项的实际版本
- 每个依赖项的依赖项
- 已解决的软件包中用校验和验证软件包的完整性
既然lockfile中已经列出了所有的依赖项,拿为什么还要将它们写在package.json中呢?为什么我们需要两个文件?
package.jsonvs.LockFile
package.json中dependencies字段显示你的项目应该安装的依赖项,但不显示这些依赖项的依赖项。依赖项可以指定精确版本或semver范围。对于semver范围,npm或yarn将h会选择最适合的版本。
这意味着,如果在发布新版本时多次运行npminstall,有可能会得到相同版本的依赖项。例如用npminstalltwilio安装twilio这样的依赖项,那么package.json中的依赖项可能会存在类似于这样的条目:
{ "dependencies":{ "twilio":"^3.30.3" } }
如果你查阅npm网站上的semver文档,就会看到^意味着任何大于3.30.3的版本和小于4.0.0都是有效版本。因此,如果在发布新版本时你没有锁定文件,npminstall或yarninstall会自动安装一个,你的package.json将不会被更新。但是lockfile的内容会有所不同。
如果npm或yarn找到它们各自的lockfile,将使用它们代替模块安装。这对于持续集成(CI)等情况尤其有用。对于此这种场景,你可以针对相应的包管理器使用特殊命令或标志:
npmci#willinstallexactlywhat'sinthepackage-lock.json yarninstall--frozen-lock-file#willinstallexactlywhat'sinyarn.lockwithoutupdatingit
当你在构建Web程序或服务器之类的应用时,这非常有用,因为我们希望在CI环境中模拟用户的行为。因此,如果在源代码控制(如git)中跟踪我们的lockfile,就可以确保每个开发人员以及服务器或构建系统还有CI系统都能够使用相同版本的依赖项。
那么当我们编写要发布到npm的库时,为什么不能做同样的事呢?要回答这个问题,首先要讨论发布的工作原理。
如何发布模块
与某些人想的相反,你发布到npm的内容并不总是与GitHub上或项目中的内容完全相同。发布模块的方式是npm将通过检查package.json和.npmignore文件中的files键或者如果没有``来确定应该发布的文件。gitignore文件。还有一些文件总是包含在内,有些文件将永远被排除在外。你可以在[npmpage](https://docs.npmjs.com/files/package.json#files)上找到这些文件的完整列表。例如,.git`目录始终会被忽略。
之后npm将会获取文件列表,并用npmpack将它们一起打包成tarball。如果要查看打包的文件,可以在项目中运行npmpack--dry-run,能看到包含所有文件的输出。
那个tarball将被上传到npm注册表。运行此命令时你可能会注意到加入你已经有了一个package-lock.json,它实际上没有被捆绑。这是因为package-lock.json将始终被忽略。
这意味着如果另一个开发人员安装了你发布的软件包,他们永远不会下载你的package-lock.json,因此在安装过程中将会完全忽略它。
这可能会导致“在我的机器上能够工作”的意外,因为你的CI和开发环境可能会选择不同的依赖项版本。那么我们可以做些什么呢?
禁用lockfile并收缩包装
首先,应该停止跟踪我们的lockfile。如果你用的是git,请将以下内容添加到项目中的.gitignore文件中:
yarn.lock package-lock.json
Yarn的文档说即使你创建了库,也应该签入yarn.lock,但是如果你想确保自己能够保证与用户相同的体验,我建议将其添加到.gitignore。
你可以通过在项目里的.npmrc文件中添加以下内容来关闭package-lock.json文件的生成:
package-lock=false
对于yarn,你可以通过添加yarninstall--no-lockfile标志保证不生成lockfile。
摆脱了package-lock.json并不意味着无法固定我们所拥有的依赖关系和子依赖关系。我们可以用另一个名为npm-shrinkwrap.json的文件。
它与package-lock.json基本相同,并由npmshrinkwrap生成并实际的打包并发布到npm注册表中。
因此,通过将npmshrinkwrap添加到npm脚本作为prepack脚本甚至是gitcommithook,可以确保在你的开发环境中,与你的用户和CI中使用相同版本的依赖项。
**一个重要的提示:**通过使用shrinkwrap文件,你可以确定精确的版本,但它也会阻止人们获得自动安装的关键补丁程序。npm强烈反对库的shrinkwrap的用例。
了解更多信息
不幸的是,虽然npmdocs中有很多相关内容,但有时很难找到你想要的东西。如果你想更好地了解安装或打包的内容,那么你一个常见标志就是--dry-run。运行该命令而不会影响你的系统。
例如npminstall--dry-run并不会将依赖项安装到你的文件系统,或者npmpublish--dry-run实际上也不会发布该包。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。