一个 Node.js 项目通常有大量的依赖,但它们并不是平等的。package.json 中将依赖划分为不同类型,是为了让包管理器和团队成员能清晰地知道:哪些代码是线上运行时必不可少的,哪些只是开发阶段用的工具,哪些是对使用者的环境要求。掌握这些分类,是规范管理项目依赖的第一步。
20.3.1 生产依赖(dependencies)
生产依赖是项目在生产环境下运行所必需的第三方包。如果某个包被删除会导致应用启动失败或核心功能出错,那它就应当放在 dependencies 中。
{
"dependencies": {
"express": "^4.18.2",
"axios": "^1.4.0",
"lodash": "^4.17.21"
}
}
安装命令:npm install <package> 会默认写入 dependencies。
何时用到:用户安装你的包时,npm install 会根据 dependencies 自动安装这些包;部署上线时只要执行 npm ci --production(yarn 则是 --prod),就能只安装生产依赖,从而减小体积。
真实例子:Web 框架(Express、Koa)、HTTP 请求库(axios)、工具函数库(lodash、dayjs)、数据库驱动等都是典型的生产依赖。
20.3.2 开发依赖(devDependencies)
开发依赖是仅在开发过程中需要的工具或库。它们用于代码检查、测试、构建、转译等任务,一旦项目构建完成并上线,这些包就不再参与运行。把这类包放到 devDependencies 中可以减少生产环境安装的包数量和体积。
{
"devDependencies": {
"eslint": "^8.45.0",
"prettier": "^3.0.0",
"jest": "^29.6.0",
"webpack": "^5.88.0",
"typescript": "^5.1.6"
}
}
安装命令:npm install <package> --save-dev 或 npm install -D <package>。
何时用到:当你在本地开发、执行测试、运行打包命令时,这些包必不可少;但部署到服务器后,它们完全可以被忽略。
真实例子:
- 构建工具:Webpack、Vite、Rollup、Gulp
- 测试框架:Jest、Vitest、Mocha、Playwright
- 代码质量:ESLint、Prettier、stylelint
- 类型系统:TypeScript(绝大多数场景放在 devDependencies)
- CSS 预/后处理器:Sass、PostCSS
20.3.3 同伴依赖(peerDependencies)
peerDependencies 用来声明宿主环境需要满足的依赖版本。它不会自动安装,而是告诉使用者:“如果你想正常使用这个插件,你必须在自己的项目中安装某个特定版本的包。” 常见于插件体系,比如一个为 React 设计的组件库需要依赖 React,但又不应该把 React 打包进组件库中(避免多个副本冲突),这时就声明 peerDependencies 让使用者自行提供 React。
{
"peerDependencies": {
"react": ">=16.8.0"
}
}
安装行为:npm 7+ 默认会自动安装 peerDependencies,但早期版本(npm 4-6)只警告不安装。Yarn 和 pnpm 也有各自的处理逻辑,但核心理念一致:peer 依赖不应该由库作者硬性捆绑,而应由最终消费者选择版本,保证单一实例。
真实例子:
- 任何 React 组件库(antd、material-ui)都会将 react 和 react-dom 声明为 peerDependencies。
- 构建工具插件,如
eslint-plugin-react需要 eslint 作为 peer 依赖。 - Babel 插件需要
@babel/core作为 peer 依赖。
注意事项:版本范围要求不能太死板,否则容易造成用户安装困难。如果插件支持多个主版本,可使用 || 指定范围,如 "react": "^16.8.0 || ^17.0.0 || ^18.0.0"。
20.3.4 可选依赖(optionalDependencies)
可选依赖是一种特殊的生产依赖:当包的安装因为某些原因(如系统不兼容、缺失编译环境)失败时,npm 仍然会继续安装过程,不会抛出错误。程序运行时可以根据该包是否存在来决定是否启用某个功能。
{
"optionalDependencies": {
"fsevents": "^2.3.0"
}
}
真实例子:
fsevents是 macOS 上用于监听文件系统事件的包,在 Windows 和 Linux 上安装可能会失败,但它被声明为 optional,因此跨平台安装不会中断。- 某些性能监控或数据库驱动也可能作为可选依赖,允许应用降级运行。
注意:可选依赖仍会被安装(如果环境支持),所以它还是写在 dependencies 区块中,但因为有容错机制,适合处理平台特定或硬件相关的依赖。不要在 optionalDependencies 中放核心功能包,否则一旦安装失败就会导致运行时异常。
20.3.5 各类依赖的安装与剥离实践
在实际项目中,我们要正确区分依赖类型,同时结合 npm prune 或 --production 标志来清理依赖:
- 打包部署时执行
npm ci --production,只安装 dependencies 和 optionalDependencies。 - 使用 Docker 构建镜像时,尽量在最终镜像中只保留生产依赖,大幅缩小镜像体积。
- 定期执行
npm prune移除不再出现在package.json中的无用包。
理解并正确设置这四种依赖类型,能帮助你维护一个干净、高效的工程依赖树,这也是工程化成熟度的重要体现。