3、依赖管理基础:requirements.txt、package.json、依赖版本号语义化
说实话,依赖管理这事儿,看着简单,坑是真不少。
我刚入行那会儿,觉得不就是列个清单嘛,有啥难的?结果被坑过几次之后,才老老实实回来补课。今天咱们就把这块儿彻底聊透。
3.1 为什么需要依赖管理?
你想想看,一个项目少则几十个包,多则几百个。没有依赖管理,会是什么场景?
- 同事A的电脑能跑,你的电脑报错
- 昨天还好好的,今天一更新就炸了
- 部署到服务器上,发现缺了某个库
说白了,依赖管理就是给项目装一个「锁」。谁用了什么版本,清清楚楚。
核心目标就三个:
- 可复现:任何人在任何时间拉下来,环境一致
- 可追溯:知道每个包是哪个版本,为什么用这个版本
- 可升级:安全更新能快速应用,又不破坏现有功能
3.2 Python 的 requirements.txt
Python 的依赖管理,最基础的就是 requirements.txt。我习惯把它放在项目根目录。
一个典型的文件长这样:
flask==2.3.0
requests>=2.28.0,<3.0.0
numpy~=1.24.0
pytest>=7.0.0
生成方式也很简单:
pip freeze > requirements.txt
但这里有个坑——pip freeze 会把当前环境所有包都列出来,包括那些间接依赖。我曾经因为这个吃过亏:把整个环境的包都锁进去了,结果别人一装,版本冲突到怀疑人生。
我的建议:用 pipreqs 这个工具,它只扫描你项目里实际 import 的包,干净很多。
pip install pipreqs
pipreqs ./ --encoding=utf-8 --force
3.3 Node.js 的 package.json
Node.js 这边,依赖管理就成熟多了。package.json 是标配。
一个典型的片段:
{
"dependencies": {
"express": "^4.18.0",
"lodash": "~4.17.21",
"axios": "0.27.2"
},
"devDependencies": {
"jest": "^29.0.0",
"eslint": "^8.0.0"
}
}
这里有个关键区别:
- dependencies:生产环境要用的包,比如 express、axios
- devDependencies:开发时才用的,比如测试框架、代码检查工具
我记得有一次,同事把 jest 放到了 dependencies 里,结果生产环境部署时,光安装依赖就花了 5 分钟,还多占了几十兆空间。嗯,这种细节不注意,后面都是成本。
注意:Node.js 项目一定要提交 package-lock.json 或 yarn.lock。这个文件锁定了所有依赖的精确版本,是「可复现」的关键。
3.4 依赖版本号语义化(SemVer)
版本号这东西,看着简单,但很多人理解错了。语义化版本号(SemVer)的格式是:
主版本号.次版本号.修订号
举个例子:2.3.1
| 位置 | 名称 | 什么时候增加 | 举例 |
|---|---|---|---|
| 第一位 | 主版本号 | 不兼容的 API 修改 | 1.x.x → 2.x.x |
| 第二位 | 次版本号 | 向下兼容的功能新增 | 2.1.x → 2.2.x |
| 第三位 | 修订号 | 向下兼容的问题修复 | 2.3.0 → 2.3.1 |
在 Python 和 Node.js 中,版本约束符号的含义略有不同,但核心逻辑一致:
| 符号 | 含义 | Python 示例 | Node.js 示例 |
|---|---|---|---|
== / 无符号 |
精确版本 | flask==2.3.0 |
"express": "4.18.0" |
>= |
大于等于 | requests>=2.28.0 |
— |
^ |
兼容主版本 | — | "express": "^4.18.0" |
~ |
兼容次版本 | numpy~=1.24.0 |
"lodash": "~4.17.21" |
避坑指南:我曾经遇到过一个问题——项目里用了 ^4.18.0,结果半年后自动装上了 4.20.0,里面有个 API 行为变了,导致线上报错。从那以后,对于核心依赖,我倾向于用精确版本或者 ~ 约束。
3.5 知识体系总览
下面这张图,帮你把本章的核心逻辑串起来:
3.6 实战建议
最后,给你几个我踩过坑之后总结出来的建议:
- 锁定版本文件一定要提交到 Git——不管是
requirements.txt还是package-lock.json,别忽略它们 - 定期更新依赖——我一般每个月跑一次
pip list --outdated或npm outdated,看看哪些包需要升级 - 升级前先看 changelog——特别是主版本号变化的时候,一定要确认有没有破坏性变更
- 用虚拟环境隔离项目——Python 用 venv,Node.js 用 nvm,别把所有依赖装到全局
一个小技巧:在 requirements.txt 里,我习惯把核心依赖写在前面,并加上注释说明为什么用这个版本。比如:
# 核心框架,v2.3.0 修复了关键安全漏洞
flask==2.3.0
# 必须 >=2.28,因为用了 v2.28 新增的 Session 特性
requests>=2.28.0
这样半年后回头看,自己也能想起来当初为什么这么选。
依赖管理这事儿,说白了就是「把不确定性降到最低」。你花 10 分钟把依赖理清楚,后面能省下 10 个小时的排查时间。这笔账,怎么算都划算。