Node.js 包管理(npm)

npm(Node Package Manager)是世界上最大的开源软件仓库——上百万个第三方库,几乎你想要的功能都有现成包。装一个库就 npm install 几秒钟搞定,这种"组装式"开发效率是 Node 最大的红利。这一章把 npm 工程化的方方面面讲透。

1. npm 是什么?

npm 做三件事:

装完 Node 你就自动有了 npm,不用单独装。下面从最常用的命令开始。

2. 安装依赖:install 全家桶

# 初始化项目(生成 package.json)
npm init -y

# 安装包:默认装进 dependencies
npm install express
# 简写:npm i express

# 安装多个
npm i express cors body-parser

# 安装指定版本
npm i express@4.18.0
npm i express@^4.18.0     # 兼容 4.18.x 的最新版
npm i express@latest       # 最新版

# 安装开发依赖(只在开发时用,不进生产)
npm i --save-dev nodemon
# 简写:npm i -D nodemon

# 全局安装命令行工具
npm i -g pm2
# 全局包可以在任意目录直接敲命令名运行

# 卸载
npm uninstall express
# 简写:npm un express

dependencies vs devDependencies是新手最常混淆的点:

判断标准:"这个包 import 到运行代码里了吗?"是 → dependencies;只在开发流程(build/test/lint)用 → devDependencies。

3. package.json:项目的身份证

每个 Node 项目根目录都有一个 package.json,它是项目的"身份证",记录项目信息和依赖:

{
  "name": "my-app",                 // 项目名(小写、无空格)
  "version": "1.4.2",               // 语义化版本号
  "description": "我的应用",
  "main": "index.js",               // 入口文件(被 require 时加载)
  "type": "module",                 // module 用 ESM,commonjs 用 CJS
  "scripts": {
    "dev": "nodemon index.js",
    "start": "node index.js",
    "build": "vite build",
    "test": "jest",
    "lint": "eslint ."
  },
  "dependencies": {                  // 生产也要用
    "express": "^4.18.2"
  },
  "devDependencies": {               // 只在开发时用
    "nodemon": "^3.0.0",
    "jest": "^29.0.0"
  },
  "engines": {
    "node": ">=18.0.0"               // 指定 Node 版本要求
  }
}

node_modules 不要提交 git——它可能几百 MB 甚至几 GB,而且可以通过 package.json + package-lock.json 完全还原。把它加进 .gitignore

4. scripts:自定义命令

scripts 是 package.json 最实用的字段——把常用命令起个简短名字,用 npm run xxx 执行:

// package.json 的 scripts 字段是最常用的功能
// 它让你用 "npm run 命令名" 执行任意 shell 命令

{
  "scripts": {
    "dev": "nodemon index.js",
    "start": "node index.js",         // start 特殊:可直接 npm start
    "test": "jest",
    "build": "vite build",
    "lint:fix": "eslint . --fix"
  }
}

# 运行脚本
npm run dev              # 任意脚本都用 npm run
npm start                # start 可省略 run(快捷别名)
npm test                 # test 同样可省略

# 串行/并行
# 串行用 &&
npm run lint && npm run test

# 并行用 &
npm run watch:css & npm run watch:js

好处是统一团队命令——不管你的项目用 webpack 还是 vite、测试用 jest 还是 mocha,团队成员只要敲 npm run dev / npm run build / npm test 就行,不用记各种工具的具体参数。新人入职看一眼 package.json 的 scripts,就知道这个项目怎么跑。

5. npx:一次性执行命令

npx 用来执行 npm 包提供的命令行工具,不需要先全局安装。这对脚手架工具特别有用:

# npx:执行包的命令,不用先全局安装
# 临时执行一次性命令,执行完不残留
npx create-react-app my-app
npx create-vite latest
npx degit user/repo my-folder

# 也用于执行本地 node_modules 里的命令(等价于 npm run)
npx eslint .

# 用指定版本的包
npx -p package@1.0.0 command-name

# 现代替代:npm 7+ 内置了类似能力
npm create vite@latest    # 等价于 npx create-vite

npx 会临时下载包、执行命令、执行完不残留。比"全局装一堆工具"干净得多。

6. 语义化版本号(SemVer)

npm 的版本号遵循 SemVer 规则:主版本.次版本.修订号(如 1.4.2)。理解它才能正确管理依赖:

# 语义化版本号(SemVer):主版本.次版本.修订号
#   1   .   4   .   2
# major   minor   patch

# major(主版本):不兼容的破坏性更新(从 1.x 升到 2.x 要改代码)
# minor(次版本):向后兼容的新功能(从 1.4 升到 1.5 不会破)
# patch(修订):向后兼容的 bug 修复(从 1.4.2 升到 1.4.3)

# package.json 里的版本前缀
"express": "4.18.2"      # 必须精确这个版本
"express": "^4.18.2"     # ^ 兼容:>=4.18.2 <5.0.0(允许 4.x 内升级)
"express": "~4.18.2"     # ~ 近似:>=4.18.2 <4.19.0(允许 4.18.x)
"express": "*"            # 任意版本(危险,别用)
"express": ">=4.0.0"      # 大于等于 4.0.0

package.json 里 ^~ 前缀的含义必须搞清楚——它决定了 npm install 时能升到什么版本。生产项目推荐用 ^(允许次版本升级,既安全又能拿到 bug 修复)。

7. lock 文件:锁住依赖

这是工程化最容易踩坑的地方。package.json 写的是版本范围(如 ^4.18.2),不同时间 npm install 装到的具体版本可能不同——这会导致"在我电脑上能跑、在你那不行"。lock 文件就是来解这个的:

# package-lock.json 的作用:锁定确切版本

# 假设 package.json 写 "express": "^4.18.2"
# 你 npm install 时,可能装了 4.18.2、4.18.3、4.19.0...
# 不同时间安装,版本可能不同 → "在我电脑上是好的"问题

# package-lock.json 记录了:
#   express 实际装的是 4.19.2
#   express 的依赖的依赖也都精确到具体版本
#   整棵依赖树的精确状态

# 别人 clone 你的项目后 npm install
# npm 会读 lock 文件,装出和你完全一致的依赖版本

# 重要习惯:
#   ✅ 提交 package-lock.json 到 git
#   ✅ 删除 node_modules 后能 npm install 复现
#   ❌ 别手改 package-lock.json
#   ❌ 别把 node_modules 提交到 git(太大)

所以团队协作铁律:提交 lock 文件。npm 用 package-lock.json,yarn 用 yarn.lock,pnpm 用 pnpm-lock.yaml。一个项目里只用一个包管理器,不要 npm 和 yarn 混着用,lock 会打架。

8. 升级与维护

# 查看过期的依赖
npm outdated
# Package     Current  Wanted  Latest
# express      4.18.2  4.19.2  5.0.0
# react       17.0.2  17.0.2  18.2.0

# 升级到 Wanted 范围内的最新(安全)
npm update

# 升级到 Latest(可能破坏性)
npm i express@latest

# 大版本升级用专用工具(自动改代码)
npx npm-check-updates -u     # 把 package.json 全升到 latest
npm install

# 删除 node_modules 重装(修复怪问题)
rm -rf node_modules package-lock.json
npm install

升级分两种:npm update 在 SemVer 范围内升级(相对安全);跨大版本升级(如 express 4 → 5、react 17 → 18)要小心,通常有 breaking changes,要看迁移指南。大项目升级前务必有完整测试覆盖。

9. 发包:把自己写的库发到 npm

# 1. 注册账号:到 npmjs.com 注册
# 2. 命令行登录
npm login
#   输入用户名、密码、邮箱、OTP(开了两步验证)

# 3. 准备 package.json
#   name 必须全网唯一(scoped 包用 @前缀避免冲突)
#   比如 "@yourname/myutils"
#   main 指向入口文件
#   version 必须是新版本(已发的不能重复)

# 4. 发布
npm publish

# scoped 包默认是私有的(收费),公开发布要加参数
npm publish --access public

# 5. 更新版本后再发
#   先改 version,再 npm publish
#   或用 npm version 命令自动改
npm version patch    # 1.0.0 → 1.0.1
npm version minor    # 1.0.0 → 1.1.0
npm version major    # 1.0.0 → 2.0.0
npm publish

发包是参与开源社区的基本功。从一个工具函数、一个组件库开始,你的代码就能被全世界开发者用到。

10. pnpm / yarn:替代选择

npm 不是唯一选择。其他流行的包管理器:

初学用 npm 就够了;项目大了、磁盘吃紧、或做 monorepo,再考虑 pnpm。

小结

npm 是 Node 工程化的命脉。记住几条:dependencies 放生产、devDependencies 放开发;scripts 统一团队命令;提交 lock 文件;SemVer 的 ^ ~ 含义;node_modules 永远别提交。下一篇学最经典的 Web 框架 Express,把前面学的 HTTP 模块包装成优雅的 API 服务。

← 上一篇 Node.js 异步编程

下一篇 Node.js Express 框架

✈️💬