1. 概述

npm(Node Package Manager)是 JavaScript 生态中最流行的依赖管理工具。在日常开发中,我们经常使用两个命令来安装依赖:npm installnpm ci。虽然这两个命令看起来相似,但在使用场景和行为上有显著差异。

本文将详细对比这两个命令的异同,并说明它们各自适合的应用场景,帮助你在开发流程和自动化构建中做出更合适的选择。


2. npm install 命令

在 JavaScript 或 Node.js 项目中,npm install 是最常用的依赖安装命令。它会根据 package.json 文件中定义的依赖项安装所需的包。

{
  "name": "some-project",
  "version": "1.0.0",
  "dependencies": {
    "lodash": "^4.17.21"
  },
  "devDependencies": {
    "eslint": "^8.1.0"
  }
}

上面的例子中,我们使用了 caret(^)符号来定义版本范围,表示允许安装与当前版本兼容的更新版本(如 minor 或 patch 级别更新)。

2.1. npm installpackage-lock.json

如果没有 package-lock.json 文件,npm install 会根据 package.json 中的版本范围进行安装,并自动生成 package-lock.json 文件,记录安装的具体版本和来源。

例如:

    "node_modules/lodash": {
      "version": "4.17.21",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
      "license": "MIT"
    },    
    "node_modules/eslint": {
      "version": "8.57.1",
      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
      "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
      "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
      "dev": true,
      "license": "MIT"
    }

⚠️ 注意:npm install 安装的是符合版本范围的最新版本。这意味着即使你没有改动 package.json,多次运行 npm install 也可能安装不同版本的依赖。

2.2. 更新 package-lock.json

package.json 中的依赖版本发生变化时,npm install 会根据新的版本范围重新解析依赖,并更新 package-lock.json

例如,将 eslint 的版本从 ^8.1.0 更新为 ^9.0.0 后:

  "devDependencies": {
    "eslint": "^9.0.0"
  }

再次运行 npm installpackage-lock.json 中的 eslint 版本也会更新为最新的 9.x。

✅ 小结:npm install 更适合开发阶段,它会根据版本范围灵活安装依赖,并自动更新 lock 文件。


3. npm ci 命令

从 npm v5.7.0 开始,引入了 npm ci 命令,用于在构建环境中进行可重复、干净的安装

npm install 不同,npm ci 会:

  • 删除已有的 node_modules 并重新创建
  • 仅根据 package-lock.jsonnpm-shrinkwrap.json 安装依赖
  • 如果 package.json 与 lock 文件不一致,会直接报错 ❌

3.1. 一个典型错误场景

假设我们在 package.json 中手动将 lodash 版本改为 ^4.16.0,但 package-lock.json 仍记录的是 4.17.21

  "dependencies": {
    "lodash": "^4.16.0"
  }

此时运行 npm ci 会报错:

$ npm ci
npm ERR! code EUSAGE
npm ERR! `npm ci` can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with `npm install` before continuing.
npm ERR! Invalid: lock file's [email protected] does not satisfy [email protected]

⚠️ 报错明确指出版本不一致。这正是 npm ci 的设计目标:确保构建环境的依赖版本与开发环境完全一致

3.2. 推荐使用场景

✅ 推荐在以下场景使用 npm ci

  • CI/CD 构建流程
  • 项目首次 clone 后的依赖安装
  • 生产环境部署

4. npm install vs. npm ci:核心区别总结

特性 npm install npm ci
使用场景 本地开发调试 构建/部署/CI
是否需要 lock 文件 ✅ 是
版本不一致处理 自动更新 lock 文件 ❌ 报错
是否允许局部安装 ✅ 支持添加/更新单个包 ❌ 只能全量安装
是否删除 node_modules ❌ 保留 ✅ 删除并重建
是否修改 lock 文件 ✅ 可能修改 ❌ 绝不修改
依赖检查 ✅ 会检查更新 ❌ 跳过检查

✅ 总结建议:

  • 开发阶段使用 npm install
  • 构建/部署阶段使用 npm ci

5. 结论

npm installnpm ci 都是管理依赖的重要命令,但它们的使用场景和行为有显著差异:

  • npm install 更灵活,适合本地开发时使用,能自动更新依赖和 lock 文件
  • npm ci 更严格,确保构建环境中的依赖与开发环境完全一致,适用于 CI/CD、部署等场景

理解它们的区别,有助于提升项目的稳定性与可维护性,避免因依赖版本不一致导致的“本地能跑,线上报错”的踩坑问题。


原始标题:npm install vs. npm ci