"npm install"和"npm ci"有什么区别?

693

我正在使用持续集成,发现了 npm ci 命令。

我不知道在我的工作流中使用此命令的优势是什么。

它会更快吗?它是否会让测试更加艰难,好还是一般?

9个回答

944

来自npm ci的官方文档

简而言之,使用npm install和npm ci的主要区别如下:

  • 项目必须有现有的package-lock.json或npm-shrinkwrap.json。
  • 如果包锁中的依赖关系与package.json中的依赖关系不匹配,则npm ci将报错,而不是更新包锁。
  • npm ci只能一次性安装整个项目:不能使用此命令添加单个依赖项。
  • 如果已经存在node_modules,则在npm ci开始安装之前会自动删除它。
  • 它永远不会写入package.json或任何包锁:安装基本上是冻结的。

基本上,npm install 会读取 package.json 文件创建一个依赖列表,并使用 package-lock.json 来决定安装这些依赖的哪些版本,如果某个依赖在 package-lock.json 中不存在,它会被 npm install 添加进去。 npm ci(也被称为清洁安装)主要用于自动化环境——例如测试平台、持续集成和部署——或是需要确保干净安装依赖项的任何情况。
它直接从package-lock.json中安装依赖项,并仅使用package.json来验证是否存在不匹配的版本。如果有任何缺失或不兼容的依赖关系,将会抛出错误。 使用npm install添加新的依赖项,并在项目中更新依赖项。通常,在拉取更新依赖项列表的更改后,在开发过程中使用它,但在这种情况下使用npm ci可能是一个好主意。
如果需要确定性、可重复的构建,请使用npm ci。例如,在持续集成、自动化作业等情况下第一次安装依赖项时,而不是使用npm install

npm install

安装一个包及其所有依赖项。 依赖项由npm-shrinkwrap.json和package-lock.json驱动(按顺序)。 没有参数:安装本地模块的依赖项。 可以安装全局软件包。 将安装node_modules中缺少的任何依赖项。 它可能会写入package.json或package-lock.json。 当与参数一起使用(npm i packagename),它可能会写入package.json以添加或更新依赖项。 当没有参数时(npm i),如果某些依赖关系尚未在此文件中,则可能会写入package-lock.json以锁定某些依赖项的版本。 npm ci
  • 至少需要 npm v5.7.1
  • 需存在 package-lock.jsonnpm-shrinkwrap.json
  • 如果这两个文件的依赖项与 package.json 不匹配,则抛出错误。
  • 删除 node_modules 并一次性安装 所有依赖项
  • 它从不写入 package.jsonpackage-lock.json

算法

虽然npm cipackage-lock.jsonnpm-shrinkwrap.json生成整个依赖树,但npm install使用以下算法(source)更新node_modules的内容:

load the existing node_modules tree from disk
clone the tree
fetch the package.json and assorted metadata and add it to the clone
walk the clone and add any missing dependencies
  dependencies will be added as close to the top as is possible
  without breaking any other modules
compare the original tree with the cloned tree and make a list of
actions to take to convert one to the other
execute all of the actions, deepest first
  kinds of actions are install, update, remove and move

5
我不知道“npm install” 可以写入 package.json 文件。你知道它能在这里写入什么吗? - Veve
9
可能有点误导了,实际上当你使用它来安装、更新或删除依赖项时,它会写入 package.json 文件。我会在文本中更明确地表达,谢谢! - lucascaro
27
npm install package 命令会修改 package-lock.jsonpackage.json 两个文件,而没有参数的 npm install 命令只会修改 package-lock.json 文件。 - knobo
4
@Link14 的 devDependencies 安装受到 --production 标志或 NODE_ENV 环境变量的控制,适用于 npm inpm ci - lucascaro
4
在文档中没有明确说明,但npm ci中的ci更好地理解为"clean install"而不是"continuous integration"。 - William Denman
显示剩余6条评论

86

npm ci会删除现有的node_modules文件夹,并依靠package-lock.json文件安装每个包的特定版本。它比npm install快得多,因为它跳过了一些特性。它干净的状态安装非常适合于ci/cd管道和docker构建!您还可以使用它一次性安装所有内容而不是具体的包。


15
删除已存在的node_modules可能会导致构建速度变慢。 - jontro
10
或许不要提交 node_modules? - Cameron Gilbert
2
@jontro,我的同事在 Docker 中测量了命令,并发现对于我们的包,ci 比 install 快得多(40 秒 vs 130 秒),但你的情况可能会有所不同。 - Michael Freidgeim
3
@MichaelFreidgeim是指在已有的node_modules的基础上还是从零开始进行操作? - jontro
1
@MichaelFreidgeim 当然。我的评论只是为了提醒如果您不是从干净的状态下运行它,它会变慢。这取决于设置方式,有一些缺点。 - jontro
显示剩余2条评论

44

虽然其他人已经回答了技术上的差异,但没有解释在哪些情况下使用两者。

它们应该在不同的情况下使用。

npm install 适用于开发和 CI 中需要缓存 node_modules 目录的情况。 何时使用这个?如果你正在制作一个供他人使用的软件包(在此类发布中不包括node_modules)。关于缓存,请注意,如果您计划支持不同版本的Node.js,请记住由于Node.js运行时需求之间的差异,可能需要重新安装node_modules。如果希望坚持使用一个版本,请坚持使用最新的LTS

npm ci 应该在测试和发布生产应用程序(最终产品,不是用于其他软件包)时使用,因为重要的是让您的安装尽可能确定性,这将花费更长的时间,但最终会使您的应用程序更可靠(在此类发布中包括node_modules)。坚持使用Node.jsLTS版本。

npm inpm ci 都利用 npm 缓存,如果存在的话,此缓存通常位于 ~/.npm

此外,npm ci 会尊重 package-lock.json 文件。与 npm install 不同的是,后者会重写该文件并始终安装新版本。

另外奖励提示:您可以根据需要混合使用它们。在 git 的功能分支上,您可以缓存 node_modules 以提高团队的生产力,并在合并请求和主分支上依赖于 npm ci 实现确定性结果。


11
除非想要更新依赖项,否则我认为没有任何情况下应该使用npm i而不是npm cinpm ci始终更好,因为确定性行为始终更好。 - enanone
1
正如我所说,npm i 会缓存以提高速度,而 npm ci 则较慢,因为它会进行完整的重新安装。它们都很有用。 - basickarl
2
npm ci 只要每个包都在 npm 缓存中,就和速度一样快。 - enanone
5
在我的情况下,即使使用本地NPM缓存,重复执行 npm ci 也明显比 npm install 慢很多:对于同一个项目,npm install 大约需要2秒,而 npm ci 则需要16秒。在清除缓存之后,它们的速度相同。我们迫切需要一种可以从 package-lock 安装但不会先删除 node_modules 的解决方案: https://github.com/npm/cli/issues/564 - Piedone
3
@Piedone 我认为删除 node_modules 是一种功能,因为我曾经看到过开发人员在其中进行了一些自定义更改,并想知道为什么在生产环境中无法工作。我总是更喜欢一个命令能够反复产生完全相同的结果,而不是表面上更快的命令。然而,npm install 可能会修改 package-lock.json 的事实对于依赖管理器来说是一个非常糟糕的默认设置,也是 npm ci 的一个巨大优势。我甚至在我的开发机器上都只使用 ci 进行纯安装,而只有当我想要更新或添加新的依赖项时才使用 install - k0pernikus
显示剩余2条评论

23
  • npm ci - 安装 package-lock.json 列出的确切版本
  • npm install - 不更改 package.json 中任何版本,使用 package.json 创建/更新 package-lock.json,然后安装 package-lock.json 列出的确切版本
  • npm update - 更新 package.json 中的包到最新版本,然后使用 package.json 创建/更新 package-lock.json,然后安装 package-lock.json 列出的确切版本

或者用另一种方式表述,npm ci 不更改任何包文件,npm install 更改 1 个包文件,npm update 更改 2 个包文件。


9
您链接的文档概述如下:
简而言之,使用npm install和npm ci的主要区别在于:
- 项目必须具有现有的package-lock.json或npm-shrinkwrap.json。 - 如果包锁中的依赖项与package.json中的依赖项不匹配,则npm ci将退出并显示错误,而不是更新包锁。 - npm ci只能一次性安装整个项目:无法使用此命令添加单个依赖项。 - 如果已经存在node_modules,则在npm ci开始安装之前将自动删除它。 - 安装实际上是被冻结的,因此它永远不会写入package.json或任何包锁。

7

这两个命令的功能很相似,但不同之处在于处理安装 package.jsonpackage-lock.json 文件中指定的依赖项的方式。

npm ci 将对应用程序的所有依赖项执行干净的安装,而 npm install 则可能会跳过某些已经存在于系统上的安装。如果已经安装在系统上的版本与 package.json 中指定的版本不同,那么问题可能会出现,即安装的版本与“所需”版本不同。

其他区别在于,npm ci 永远不会更改你的 package*.json 文件。如果 package.jsonpackage-lock.json 文件中的依赖项版本不匹配,则它将停止安装并显示错误信息。

您可以从官方文档此处阅读更好的解释。

此外,您可能还想在此阅读有关软件包锁的信息。


5
值得注意的是,像alpine这样的轻量级节点Docker镜像没有安装Python,而node-gypnpm ci所依赖的。
我认为需要在构建中安装Python作为依赖项才能使npm ci正常工作有些主观。
更多信息请参见 Docker 和 npm - gyp ERR!not ok

2
根据您发布的问题,他们遇到了react-scripts的问题,并非由于npm ci。从我所找到的信息来看,在npm ci中没有依赖于python - Andrew McC

1

它执行干净的安装,适用于您需要删除 node_modules 并重新运行 npm i 的情况。

我不知道为什么有些人认为它是“持续集成”的缩写。有一个可以作为 npm i 运行的 npm install 命令,还有一个可以作为 npm ci 运行的 npm clean-install 命令。


1

npm install是安装项目package.json文件中列出的依赖项的命令,而npm ci则是从package-lock.json或npm-shrinkwrap.json文件中安装依赖项的命令。通常在持续集成(CI)环境中使用npm ci命令,其中package-lock.json或npm-shrinkwrap.json文件被检入版本控制并且不应被修改。由于npm ci从锁定文件中安装依赖项,因此它比npm install更快且更可靠,后者根据package.json文件的状态可能会安装不同版本的依赖项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接