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 知识体系总览

下面这张图,帮你把本章的核心逻辑串起来:

依赖管理知识体系 Python requirements.txt pip freeze / pipreqs == >= ~= Node.js package.json dependencies / devDependencies ^ ~ 精确版本 语义化版本 主版本.次版本.修订号 不兼容 / 新功能 / 修复 向下兼容原则 三大核心目标 可复现 | 可追溯 | 可升级 任何环境、任何时间、一致可靠

3.6 实战建议

最后,给你几个我踩过坑之后总结出来的建议:

  1. 锁定版本文件一定要提交到 Git——不管是 requirements.txt 还是 package-lock.json,别忽略它们
  2. 定期更新依赖——我一般每个月跑一次 pip list --outdated 或 npm outdated,看看哪些包需要升级
  3. 升级前先看 changelog——特别是主版本号变化的时候,一定要确认有没有破坏性变更
  4. 用虚拟环境隔离项目——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 个小时的排查时间。这笔账,怎么算都划算。

可可精品资料库