CC 咖啡猫的工作空间 Coding Space

包管理器

原始素材:2-开发环境与工具/2.07-package-managers.md

这篇解决什么问题

写代码不必从零造轮子。HTTP 请求、日期处理、表单校验、数据库连接、测试框架、构建工具,大量通用能力已经有人写好并发布到公共仓库。

包管理器要解决的是:如何找到、安装、升级、锁定和协作维护这些依赖,让项目环境可复现。

理解包管理器,可以帮你解决这些问题:

  • npm install 到底做了什么。
  • 为什么同事能跑,你这里跑不起来。
  • package.json 和 lockfile 分别负责什么。
  • 为什么不应该提交 node_modules
  • 全局安装和本地安装应该怎么选。
  • 版本号里的 ^~* 有什么区别。
  • Python 为什么需要虚拟环境。

包管理器的本质

包管理器可以理解为代码生态的「应用商店 + 安装器 + 版本记录器」。

它通常负责四件事:

  1. 在 Registry 中找到包。
  2. 下载包及其依赖。
  3. 安装到项目或系统环境。
  4. 记录精确版本,保证团队环境一致。

以 JavaScript 为例:

npm install axios

背后不是只下载一个 axios,还会解析 axios 自己依赖的包,构建完整依赖树。

各生态常见包管理器

生态 常见工具 配置 / 锁文件 说明
JavaScript npm、Yarn、pnpm package.jsonpackage-lock.jsonyarn.lockpnpm-lock.yaml 前端和 Node.js 主生态
Python pip、conda、uv、poetry requirements.txtpyproject.tomluv.lock 注意虚拟环境隔离
Rust cargo Cargo.tomlCargo.lock 构建、测试、发布一体化
Go go modules go.modgo.sum 标准工具链内置
macOS / Linux Homebrew、apt、dnf / yum 系统级包数据库 安装系统工具和服务
Windows winget、Chocolatey、Scoop 各自工具配置 安装桌面软件和开发工具

不同生态命令不一样,但核心逻辑都是:声明依赖、解析版本、下载包、安装文件、记录锁定结果。

Registry:包从哪里来

Registry 是包的中央仓库。

生态 Registry 例子
JavaScript npm Registry https://www.npmjs.com
Python PyPI https://pypi.org
Rust crates.io https://crates.io
Go pkg.go.dev / module proxy https://pkg.go.dev
Homebrew formulae.brew.sh https://formulae.brew.sh
Windows winget / Chocolatey winget.runchocolatey.org

企业内部也可能搭建私有 Registry,用于发布内部包、缓存公共依赖或控制安全审计。

npm、Yarn、pnpm 对比

工具 特点 适合场景
npm Node.js 自带,生态最通用 所有项目都能用,兼容性最好
Yarn 安装速度快,历史上改善 npm 体验 已有 Yarn 项目继续使用
pnpm 硬链接共享依赖,磁盘占用低,依赖隔离严格 新项目推荐,monorepo 体验好

简化理解:

磁盘占用:pnpm < yarn PnP < npm
安装速度:pnpm ≈ yarn > npm
通用程度:npm > pnpm > yarn

实践建议:新项目可以优先考虑 pnpm;已有项目不要轻易混用包管理器。一个项目里同时出现 package-lock.jsonyarn.lockpnpm-lock.yaml,通常意味着团队依赖管理已经开始混乱。

npm install 背后发生了什么

执行:

npm install axios

大致经过四步:

阶段 做什么 结果
Resolve 解析依赖树,确定要安装哪些包和版本 得到完整依赖图
Fetch 从 Registry 或本地缓存下载 tarball 得到包压缩文件
Link 解压并链接到 node_modules 项目可以引用依赖
Lock 写入 lockfile 固定精确版本

这就是为什么一次安装可能会下载很多包:你安装的是直接依赖,包管理器还要处理间接依赖。

常用命令速查

JavaScript

npm install                 # 按 package.json 安装所有依赖
npm install axios           # 安装生产依赖
npm install -D jest         # 安装开发依赖
npm uninstall axios         # 卸载依赖
npm update                  # 在版本范围内升级依赖
npm run build               # 运行 scripts 中的 build
npm ci                      # 严格按 lockfile 安装,适合 CI
npx prettier --write src/   # 临时运行工具

pnpm 对应命令:

pnpm install
pnpm add axios
pnpm add -D vitest
pnpm remove axios
pnpm run build
pnpm dlx create-vue

Python

pip install requests
pip install requests==2.28.0
pip freeze > requirements.txt
pip install -r requirements.txt

Rust

cargo add serde
cargo build
cargo test
cargo run

Go

go get github.com/gin-gonic/gin
go mod tidy
go build ./...

系统工具

brew install git
winget install Git.Git
winget upgrade --all

package.json 和 npm scripts

package.json 是 JavaScript 项目的依赖和脚本声明文件。

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "test": "vitest",
    "lint": "eslint src/"
  },
  "dependencies": {
    "axios": "^1.6.8"
  },
  "devDependencies": {
    "typescript": "^5.4.0"
  }
}

运行:

npm run dev
npm run build

npm scripts 的价值:

  • 团队统一命令入口。
  • 自动把 node_modules/.bin 加入 PATH。
  • 不需要每个人记住底层工具参数。
  • CI 可以直接复用同一套命令。

生产依赖和开发依赖

类型 字段 例子 用途
生产依赖 dependencies axiosreactexpress 运行时需要
开发依赖 devDependencies typescripteslintvitest 只在开发、测试、构建时需要

判断标准:部署后的应用运行时还需要它吗?需要就放 dependencies,只用于构建和检查就放 devDependencies

本地安装 vs 全局安装

npm install axios          # 本地安装到当前项目
npm install -g typescript  # 全局安装到系统环境
对比 本地安装 全局安装
存放位置 当前项目 node_modules 系统级目录
版本隔离 每个项目独立 全机共用
团队一致性 lockfile 保证 依赖每个人本机环境
适合对象 项目库、构建工具、测试工具 少数通用 CLI

黄金法则:

库类依赖永远本地安装;命令行工具优先本地安装,用 npxpnpm dlx 或 scripts 调用。

原因很简单:项目 A 可能需要 ESLint 8,项目 B 需要 ESLint 9。工具本地化才能让项目环境稳定。

npx、pnpm dlx 和临时运行

临时运行工具,不污染全局环境:

npx create-vue my-project
npx prettier --write src/
npx typescript@5.4 tsc --version
pnpm dlx create-vue

Python 生态里 uvx 也有类似用途:

uvx ruff check .

语义化版本

语义化版本(SemVer)格式是:

MAJOR.MINOR.PATCH

例如:

2.8.3
| | |
| | └─ PATCH:修复 Bug,通常兼容
| └─── MINOR:新增功能,通常兼容
└───── MAJOR:破坏性变更,升级要谨慎

常见版本范围:

写法 含义 例子
1.6.8 精确版本,只接受这个版本 最严格
^1.6.8 允许 minor 和 patch 升级,不升级 major 推荐常见写法
~1.6.8 只允许 patch 升级 更保守
* 任意版本 生产环境避免

最佳实践:package.json^ 声明兼容范围,lockfile 固定实际安装版本。

lockfile:团队协作的基石

如果 package.json 里写:

{
  "dependencies": {
    "axios": "^1.6.0"
  }
}

不同时间安装可能得到不同版本。lockfile 会记录实际安装的精确版本和依赖树,保证团队、CI、生产环境一致。

文件 生态
package-lock.json npm
yarn.lock Yarn
pnpm-lock.yaml pnpm
requirements.txt / lock 文件 Python
Cargo.lock Rust
go.sum Go

常见命令:

场景 命令 行为
本地开发 npm install 参考 lockfile 安装,必要时更新
CI / 生产构建 npm ci 严格按 lockfile 安装,不一致就失败
主动升级 npm update 在允许范围内升级并更新 lockfile

lockfile 要不要提交?

  • Web 应用、后端服务:必须提交。
  • npm 发布的库:视团队策略,库消费者会有自己的 lockfile。
  • Go 项目:go.sum 必须提交。
  • Python 项目:依赖声明和锁定文件应提交。

依赖地狱和幽灵依赖

依赖树可能非常大。你的项目依赖 50 个包,这 50 个包又依赖几百个包。

常见问题:

  • 版本冲突:两个包需要同一个依赖的不同主版本。
  • 重复安装:同一依赖出现多个版本。
  • 幽灵依赖:你没有在 package.json 声明,却因为被提升到顶层而能 import。
  • 安全漏洞:间接依赖里有已知漏洞。

不同生态的处理方式:

工具 处理思路
npm 依赖提升,尽量共享
pnpm 硬链接共享 + 严格隔离,减少幽灵依赖
Cargo 通过版本规则和锁文件管理冲突
Go modules 最小版本选择 MVS

Python 虚拟环境

Python 默认容易把包装到全局环境。不同项目需要不同版本时,就会互相污染。

创建虚拟环境:

python -m venv .venv

激活:

# macOS / Linux
source .venv/bin/activate

# Windows CMD
.venv\Scripts\activate

# Windows PowerShell
.venv\Scripts\Activate.ps1

安装依赖:

pip install requests

退出:

deactivate

PowerShell 如果禁止脚本执行:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

.venv 不要提交到 Git。应提交 requirements.txtpyproject.toml 或对应 lock 文件。

常见问题

node_modules 要提交到 Git 吗

不要。它体积大、平台相关、可通过 lockfile 重新安装。应写入 .gitignore

node_modules/

安装失败怎么办

先确认 Node / npm / pnpm 版本,再清理重装:

npm cache clean --force
rm -rf node_modules package-lock.json
npm install

Windows CMD:

rmdir /s /q node_modules
del package-lock.json
npm install

安装速度慢怎么办

推荐项目级配置,不污染全局:

echo "registry=https://registry.npmmirror.com" > .npmrc

Python 临时指定镜像:

pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

安全漏洞怎么办

npm audit
npm audit fix
npm audit fix --force

--force 可能引入破坏性升级,使用前要看变更和跑测试。

如何判断一个包是否值得信赖

检查这些信号:

  • 周下载量。
  • 最近更新时间。
  • GitHub Stars 和 Issues 活跃度。
  • 维护者是否可信。
  • 依赖数量是否过多。
  • 是否有安全漏洞记录。
  • 包体积是否合理。

名词对照表

英文术语 中文对照 解释
Package 包 / 库 别人写好并发布的代码模块。
Registry 注册表 / 仓库 所有包的中央存储服务。
Dependency 依赖 项目运行所需的其他包。
devDependency 开发依赖 只在开发、测试、构建阶段需要的包。
Lockfile 锁文件 记录精确版本和依赖树,保证环境一致。
SemVer 语义化版本 MAJOR.MINOR.PATCH 版本规范。
node_modules 模块目录 npm 安装依赖的实际目录。
venv 虚拟环境 Python 项目的独立依赖环境。
tarball 压缩包 包发布和下载时常见的 .tgz 格式。
Hoisting 依赖提升 npm 将部分子依赖提升到顶层目录。
Phantom Dependency 幽灵依赖 未声明却因为依赖提升而能使用的包。
npx 包运行器 临时运行 npm 包,无需全局安装。
Crate Rust 包 Rust 生态中包的单位。
winget Windows 包管理器 Windows 官方软件安装工具。

小结

  • 包管理器帮你下载、安装、管理和锁定依赖。
  • Registry 是包的来源,lockfile 是团队环境一致性的关键。
  • 项目依赖优先本地安装,少用全局安装。
  • 语义化版本要和 lockfile 配合使用。
  • Python 项目要用虚拟环境隔离依赖。
  • 依赖安全和依赖体积也是工程质量的一部分。