Skip to main content

包管理

包管理工具对比

结论先行:新项目一律用 pnpm。追求极致启动速度且项目无存量包袱可试 bunnpm 够用但磁盘效率差,yarn classic 已被时代淘汰,yarn berry 的 PnP 学习成本高、兼容性有坑。

维度npmyarn classicyarn berry (v2+)pnpm(推荐)bun
安装速度(硬链接 + 并行)极快(原生 Zig)
磁盘占用(每个项目独立副本)极低(全局 store)
锁文件package-lock.jsonyarn.lockyarn.lockpnpm-lock.yamlbun.lockb(二进制)
Monorepo支持(弱)不支持支持原生支持,体验最佳支持(较新)
幽灵依赖存在存在可关闭默认隔离存在
Node 兼容部分兼容
生态成熟度最成熟维护中较成熟成熟发展中
学习成本(PnP 概念)
适用场景老项目维护历史项目特殊需求新项目首选新项目、追求启动速度

关键概念

  • 幽灵依赖(Phantom Dependency):能 import 到但没在 package.json 声明的包。npm / yarn classic 因为把依赖平铺到 node_modules 根目录,导致子依赖能被「偷」到用,看似方便实则升级时一删就炸。pnpm 通过软链接 + 严格隔离彻底解决
  • 硬链接 / 全局 store:pnpm 把所有包下载到 ~/.pnpm-store,项目里只放硬链接。同一台机器装 100 个项目,磁盘只占 1 份。
  • PnP(Plug'n'Play):yarn berry 砍掉 node_modules,包解析靠 .pnp.cjs 映射表,理论速度最快但很多工具(webpack、jest、各种 loader)不兼容,需要 .pnp.cjs 兜底,不推荐作为默认选项

面试话术

「我们项目用 pnpm,核心三点:一是硬链接全局 store 节省磁盘,CI 上百个 job 拉同一个 store;二是非平铺的 node_modules 杜绝幽灵依赖,package.json 删哪个就真没哪个;三是原生 workspace 体验比 npm/yarn 都顺。」


一、实践指南

  1. 必须 commit lock 文件package-lock.json / pnpm-lock.yaml / yarn.lock
  2. CI 用 --frozen-lockfile:保证 lock 不被修改
  3. 依赖要定期更新pnpm outdated + Renovate 自动化
  4. 不要装大而全的包:选按需引入的(如 lodash-es 而非 lodash
  5. 循环依赖要警惕:a 依赖 b、b 依赖 a,几乎一定意味着设计有问题
  6. 私有包用私有 registry:配置 .npmrc 切到公司内网
  7. 避免 latest / *:锁版本才能保证构建可复现

二、依赖类型选择

字段装在什么环境例子
dependencies生产 + 开发react、lodash、axios
devDependencies仅开发typescript、eslint、webpack
peerDependencies由宿主提供react(类库要 react)、antd 的 peer
optionalDependencies装失败也不报错fsevents(仅 macOS)

原则:能在 devDependencies 就别放 dependencies,能 peer 就别 dependencies

三、版本号规范(SemVer)

major.minor.patch
  • major:破坏性变更
  • minor:新增功能(向后兼容)
  • patch:bugfix

锁定方式:

{
"dependencies": {
"react": "18.2.0", // 精确
"lodash": "^4.17.21", // 兼容 4.x.x
"antd": "~4.20.0", // 兼容 4.20.x
"dayjs": "latest" // 不推荐
}
}

推荐用 ^ 配合 package-lock.json 锁定精确版本,构建可复现

四、Monorepo 实践

// 根 package.json
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"]
}
monorepo/
├─ packages/
│ ├─ ui/ # 组件库
│ ├─ utils/ # 工具库
│ └─ app/ # 主应用
├─ pnpm-workspace.yaml
└─ package.json

pnpm-workspace.yaml

packages:
- "packages/*"

子包之间引用:

// packages/app/package.json
{
"dependencies": {
"@my/ui": "workspace:*",
"@my/utils": "workspace:*"
}
}

常用工具:pnpm + Turborepopnpm + nx。详见 Monorepo 实战

五、常用 script 模板

{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .ts,.tsx --fix",
"lint:check": "eslint . --ext .ts,.tsx",
"format": "prettier --write \"src/**/*.{ts,tsx,less,md}\"",
"test": "vitest",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit",
"prepare": "husky"
}
}

六、常见命令速查

按需查阅,不用记。

# npm
npm init # 初始化
npm i <pkg> # 安装到 dependencies
npm i -D <pkg> # 安装到 devDependencies
npm i -g <pkg> # 全局安装
npm i <pkg>@<version> # 安装指定版本
npm uninstall <pkg> # 卸载
npm run <script> # 运行脚本
npm list --depth=0 # 查看已安装包
npm outdated # 检查过期依赖

# pnpm(推荐)
pnpm add <pkg> # 默认装到 dependencies
pnpm add -D <pkg> # devDependencies
pnpm add -g <pkg> # 全局
pnpm add -w <pkg> # workspace 根
pnpm filter <pkg> <cmd> # 对子包执行命令

七、package.json 字段详解

字典式参考,按字段名检索。

下面列出常见字段,更详细的内容参考 npm 官方文档

{
// 包的唯一标识,npm view <包名> 查看是否被占用
"name": "",
// 遵循语义化版本规范 SemVer
"version": "",
/******* 描述信息 *******/
"description": "",
"keywords": [],
"author": "",
"contributors": [],
"homepage": "",
"bugs": { "url": "" },
"repository": { "type": "git", "url": "" },
/******* 脚本配置 *******/
"scripts": {
"start": "",
"build": "",
"test": ""
},
// 配置脚本中使用的环境变量,通过 process.env.npm_package_config_xxx 获取
"config": { "port": "8080" },
/******* 依赖配置 *******/
"dependencies": {},
"devDependencies": {},
"peerDependencies": {},
"optionalDependencies": {},
"bundledDependencies": [],
"engines": {},
/******* 文件 *******/
"main": "", // 入口
"browser": "", // 浏览器环境入口
"module": "", // ESM 入口
"bin": { "jupiter": "" }, // 可执行文件
"files": [], // publish 时包含的文件
"directories": {},
/******* 发布配置 *******/
"private": false,
"publishConfig": { "registry": "" },
"preferGlobal": true,
"os": [],
"cpu": [],
"license": "",
/******* 第三方配置 *******/
"typings": "types/index.d.ts",
"eslintConfig": {},
"lint-staged": {},
"gitHooks": { "pre-commit": "lint-staged" },
"babel": {},
"browserslist": {
"production": [">0.2%", "not dead", "not op_mini all"],
"development": ["last 1 chrome version"]
}
}

八、相关文档