6月5日 21:40

npm 依赖类型全解析:dependencies、devDependencies 和 peerDependencies 怎么选

package.json 里有 dependenciesdevDependenciespeerDependenciesoptionalDependencies——都叫依赖,到底什么区别?该往哪个里装?装错了会怎样?这篇一次讲清楚。

dependencies vs devDependencies:唯一的本质区别

生产环境装不装——就这么简单。

dependenciesdevDependencies
npm install安装安装
npm install --production安装不安装
npm ci --production安装不安装
NODE_ENV=production npm install安装不安装
  • dependencies:应用运行时必需的包(express、axios、lodash)
  • devDependencies:只在开发和构建时需要的包(jest、eslint、typescript、webpack)

怎么判断放哪里

问自己一个问题:这个包如果不在,应用还能跑吗?

  • 能跑 → devDependencies(测试框架、代码检查、构建工具)
  • 不能跑 → dependencies(Web 框架、数据库驱动、日期库)

一个容易搞混的例子

TypeScript 放哪?

  • 应用项目devDependencies——运行时不需要 TypeScript,只需要编译产物
  • 库项目(npm 包):devDependencies——用户装你的包不需要 TypeScript

@types/xxx 呢?也是 devDependencies——类型声明只在编译时用。

peerDependencies:我需要你,但我不装你

peerDependencies 是给库/插件用的,告诉宿主项目"你需要安装这个依赖,我自己不装"。

json
// react-component-lib 的 package.json { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }

为什么不直接放 dependencies?因为 React 只能有一个实例。如果组件库自己装了一份 React,应用也装了一份,运行时会有两个 React 副本——hooks 会炸。

npm v7 以前 vs 现在

  • npm v6:peerDependencies 不满足只会警告,照样安装
  • npm v7+:peerDependencies 不满足会报错,安装失败

这导致很多老项目升级 npm 后突然装不上依赖了。解决方案:

bash
npm install --legacy-peer-deps # 回退到 v6 的行为

常见需要 peerDependencies 的场景

  • UI 组件库依赖 React/Vue/Angular
  • Babel 插件依赖 @babel/core
  • ESLint 插件依赖 eslint
  • Webpack loader 依赖 webpack

原则:你的包作为插件扩展另一个包时,被扩展的包放在 peerDependencies

optionalDependencies:装不上也没关系

json
{ "optionalDependencies": { "fsevents": "^2.3.0" } }

安装失败不会中断整个 npm install——只是这个包不可用,调用时需要自己做容错:

javascript
let fsevents; try { fsevents = require('fsevents'); } catch { // 回退到其他方案 }

典型场景:fsevents 只在 macOS 上可用,Linux/Windows 上装不了但也不影响功能——用其他文件监听方案兜底。

注意:不要滥用。大部分依赖是必须的,装不上就应该报错而不是静默跳过。

bundledDependencies:打包进你的发布包

json
{ "bundledDependencies": ["my-helper-lib"] }

正常情况下 npm install 你的包时,依赖会从 registry 下载。但 bundledDependencies 里的包会被直接打包到你的发布文件中,安装时不需要从 registry 下载。

用途很少——主要是某些包不在公共 registry 上,又不想让用户单独配置私有源。

版本号规则:^ vs ~ vs 精确版本

json
{ "dependencies": { "express": "^4.18.0", "lodash": "~4.17.0", "react": "18.2.0" } }
写法允许的版本范围例子
^4.18.0兼容的次版本更新4.18.0 ~ 4.x.x(不会升到 5.0)
~4.17.0兼容的修订版本更新4.17.0 ~ 4.17.x(不会升到 4.18)
4.18.0精确版本只能用 4.18.0

^ 是默认行为(npm install 自动加),意味着次版本和修订版本的更新都会被接受。这通常没问题,但如果某个次版本更新引入了 bug,你的项目可能在别人那能跑在你这跑不了——这就是为什么需要 package-lock.json 锁定精确版本。

实际项目中的依赖配置建议

应用项目(Web 应用、后端服务)

  • dependencies:运行时必需的包
  • devDependencies:构建工具、测试、lint
  • 不需要 peerDependenciesoptionalDependencies

库项目(npm 包、组件库)

  • dependencies:库运行时必需且不会被宿主重复安装的包
  • devDependencies:构建工具、测试、文档
  • peerDependencies:宿主项目应该提供的包(React、Webpack 等)
  • optionalDependencies:平台特定的可选增强

依赖类型选择流程

shell
这个包运行时需要吗? ├── 不需要 → devDependencies ├── 需要 → 宿主项目可能已经安装了吗? │ ├── 是 → peerDependencies │ └── 否 → 装不上也行吗? │ ├── 是 → optionalDependencies │ └── 否 → dependencies
标签:NPM