npm 与包管理
npm 是 Node.js 自带的包管理器,也是目前世界上最大的软件注册表。无论你最终使用 yarn 还是 pnpm,理解 npm 的核心概念——package.json、锁文件、语义化版本——都是前端工程化的基本功。
一、初始化项目
1.1 npm init
bash
npm init # 交互式生成 package.json
npm init -y # 跳过提问,使用默认值1.2 package.json 字段详解
json
{
"name": "my-app",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.mjs",
"exports": {
".": { "import": "./dist/index.mjs", "require": "./dist/index.js" }
},
"types": "dist/index.d.ts",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": { "lodash": "^4.17.21" },
"devDependencies": { "vite": "^5.0.0" },
"peerDependencies": { "vue": "^3.0.0" }
}| 字段 | 含义 | 说明 |
|---|---|---|
name | 包名 | 发布到 npm 时唯一,不能包含大写字母 |
version | 版本号 | 遵循 semver:major.minor.patch |
main | 入口文件 | CommonJS 环境加载的入口 |
module | 模块入口 | 打包器优先使用,保留 ESM 语法便于 Tree Shaking |
exports | 导出映射 | 更精细的入口控制,支持条件导出与子路径限制 |
types | 类型声明 | TypeScript 使用的 .d.ts 入口 |
scripts | 脚本命令 | npm run <script> 执行 |
dependencies | 运行时依赖 | 生产环境需要 |
devDependencies | 开发依赖 | 构建、测试等开发时工具 |
peerDependencies | 同伴依赖 | 由宿主项目提供,用于插件/组件库(如 Vue 插件声明对 vue 的依赖) |
1.3 dependencies 与 devDependencies 区分
bash
npm install lodash # 默认写入 dependencies
npm install -D vite eslint # -D 写入 devDependencies
npm install --save-dev jest # 等价写法判断标准:代码运行(含打包产物)时需要它 → dependencies;只是开发工具 → devDependencies。安装到 dependencies 的包会进入生产环境,增加线上体积。
二、npm install 与锁文件
2.1 install 流程
bash
npm install # 按 package.json 安装全部依赖
npm install axios # 安装并写入 dependencies
npm ci # 严格按 package-lock.json 安装(CI 推荐)2.2 package-lock.json 的作用
npm install 生成的 package-lock.json 记录了每一层依赖的确切版本。因为 ^/~ 是模糊范围,两次安装可能拉到不同版本——锁文件把版本钉死,保证任何人在任何时间安装的结果一致:
| 场景 | 无锁文件 | 有锁文件 |
|---|---|---|
| 队友安装 | 可能拉到不同版本 | 完全一致 |
| 线上部署 | 版本漂移,偶发故障 | 可复现 |
| 安全审计 | 难追溯漏洞版本 | 精确知道装了哪个版本 |
bash
npm install # 生成或更新 package-lock.json
npm ci # 删除 node_modules 后按锁文件精确安装,速度快且不会改动锁文件三、semver 语义化版本
版本号格式为 主版本.次版本.补丁(major.minor.patch):
| 版本位 | 何时递增 | 示例 |
|---|---|---|
| major(主版本) | 不兼容的 API 变更 | 2.0.0 |
| minor(次版本) | 新增功能,向后兼容 | 1.5.0 |
| patch(补丁) | 修复 Bug,向后兼容 | 1.5.3 |
3.1 范围写法
json
{
"dependencies": {
"lodash": "^4.17.21", // ^:允许 minor 和 patch 更新(>=4.17.21 <5.0.0)
"react": "~18.2.0", // ~:只允许 patch 更新(>=18.2.0 <18.3.0)
"vue": "3.0.0", // 精确版本
"axios": ">=1.0.0 <2.0.0" // 区间
}
}| 写法 | 含义 | 示例 |
|---|---|---|
1.2.3 | 精确匹配 | 只能装 1.2.3 |
~1.2.3 | 锁定 minor | 1.2.x 任意补丁 |
^1.2.3 | 锁定 major | 1.x.x(>=1.2.3 且 <2.0.0) |
>=1.2.3 <2.0.0 | 显式区间 | 1.2.3 至 2.0.0 前 |
* / latest | 任意版本 | 不推荐 |
注意 ^0.x.y 的特殊性:^0.2.3 表示 >=0.2.3 <0.3.0(0 开头的次版本变更被视为不兼容),这也是早期包频繁小版本更新时容易踩的坑。
四、常用命令
| 命令 | 作用 |
|---|---|
npm install | 安装依赖 |
npm uninstall lodash | 卸载依赖(-D 区分环境) |
npm update | 按 semver 范围更新依赖 |
npm ls | 列出依赖树,排查重复安装 |
npm ls --depth=0 | 只列出顶层依赖 |
npm run | 列出所有可用 scripts |
npm run dev | 执行 scripts 中的 dev 脚本 |
npm outdated | 查看依赖是否有新版本 |
npm audit | 检查依赖安全漏洞 |
bash
npm ls lodash # 查看 lodash 被哪些包依赖、装了几份
npm run dev -- --port 3000 # 向脚本追加参数五、yarn 与 pnpm
5.1 yarn
yarn 1.x 解决了早期 npm 的痛点:更快的并行下载、缓存复用、离线安装。语法与 npm 高度类似:
bash
yarn add lodash
yarn remove lodash
yarn upgrade5.2 pnpm
pnpm 用硬链接 + 全局内容寻址存储重构了 node_modules 结构,磁盘占用与安装速度都大幅领先:
text
npm / yarn 结构(依赖提升,可能幻影依赖):
node_modules/
lodash/
vue/
node_modules/xxx # 嵌套,可能重复
pnpm 结构(符号链接 + 唯一副本):
node_modules/
.pnpm/ # 所有包的真实存储,每份只存一次
lodash -> .pnpm/lodash@4.17.21/node_modules/lodash| 维度 | npm | yarn | pnpm |
|---|---|---|---|
| 安装速度 | 中 | 较快 | 最快(全局缓存硬链接) |
| 磁盘占用 | 高(多项目重复) | 高 | 极低(共享存储) |
| node_modules 结构 | 扁平+嵌套 | 扁平 | 符号链接 |
| 幻影依赖 | 有 | 有 | 无(严格隔离) |
| 兼容性 | 最好 | 好 | 好(个别工具需配置) |
| 锁文件 | package-lock.json | yarn.lock | pnpm-lock.yaml |
幻影依赖指代码能直接 import 未声明的包(因为 npm 的依赖提升把它顶到了顶层)。pnpm 的隔离结构从根上杜绝了这个问题。
六、npx
npx 随 npm 附带,用于临时执行包命令而无需全局安装:
bash
npx vite --version # 使用 node_modules 里的 vite,没有则临时下载
npx create-vite@latest my-app # 执行脚手架
npx http-server . # 临时起一个静态服务器| 方式 | 行为 |
|---|---|
npm install -g pkg | 全局安装,污染全局环境、版本易冲突 |
npx pkg | 用完即走,自动寻找本地或临时拉取 |
七、发布包流程
7.1 发布到公共仓库
bash
npm login # 输入用户名/密码/邮箱(或 token)
npm version patch # 自动升级版本号 1.0.0 → 1.0.1 并打 tag
npm publish # 发布到 npm 官方仓库7.2 版本与发布注意事项
- 发布前检查
package.json的name(npm 全站唯一)、version、files(控制哪些文件进包); - 发布目录默认包含仓库文件,用
files或.npmignore排除测试与源码; - 测试包可用
npm pack先打包预览内容:
bash
npm pack # 生成 tgz,可查看包内容
npm publish --dry-run # 预览将要发布的文件7.3 私有仓库
公司内部包不能发布到公共 npm,两种常见方案:
| 方案 | 说明 |
|---|---|
npm config set registry | 指向自建仓库(如 Verdaccio、Nexus) |
| scope 私有包 | @company/pkg,配合 npm 付费私有仓库 |
bash
# 使用自建私服
npm config set registry http://registry.mycompany.com
# 或按项目配置 .npmrctext
# .npmrc
registry=https://registry.mycompany.com/八、常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
ERESOLVE 依赖冲突 | 版本范围互相矛盾 | 用 npm install --legacy-peer-deps 或升级依赖 |
| 装包超时 | 网络问题 | 切换国内镜像:npm config set registry https://registry.npmmirror.com |
| 幽灵依赖 | 依赖提升 | 迁移 pnpm,或在 lint 规则中禁止未声明依赖 |
npm audit 报高危 | 依赖有漏洞 | npm audit fix 自动升级修复版本 |
| 删除 node_modules 后装不上 | 锁文件与 package.json 不一致 | 删除锁文件后重新 npm install(慎用) |
npm 生态的核心是"声明式依赖 + 版本锁定 + 可复现安装"。把 package.json 当作项目清单来维护,配合锁文件提交到仓库,团队的构建就会稳定可预期。