文章

npm 包管理器

npm 包管理器

npm 入门

什么是 npm?

npm 是 Node.js 的标准包管理器。新版的 node.js 已经集成了 npm。可以通过输入 npm -v 来测试是否成功安装;如果你安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级,命令如下:

1
2
3
4
sudo npm install npm -g

# 如果是 Window 系统使用以下命令即可:
npm install npm -g

npm init

1
npm init

通过 npm init 命令为你的应用创建一个 package.json 文件,该命令要求你输入几个参数,例如此应用的名称、版本号、描述、指定的入口文件,你可以直接按回车键接受大部分默认设置即可。

npm init 命令的作用是将文件夹初始化为一个包,交互式创建 package.json 文件。 package.json 文件是包的配置文件,每个包都必须要有, package.json 文件内容:

1
2
3
4
5
6
7
8
9
10
11
{
    "name": "1-npm", #包的名字
    "version": "1.0.0", #包的版本
    "description": "", #包的描述
    "main": "index.js", #包的入口文件
    "scripts": { #脚本配置
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "", #作者
    "license": "ISC" #开源证书
}

npm 安装模块 npm install

npm 安装 Node.js 模块语法格式如下:npm install <Module Name>

示例:使用 npm 命令安装常用的 Node.js web 框架模块 express:

1
2
npm install express
# npm在哪个目录下执行命令,node_modules目录就在哪生成

安装好之后,express 包就放在了工程目录下的 node_modules 目录中,因此在代码中只需要通过 require(‘express’) 的方式就好,无需指定第三方包路径。

1
var express = require('express');

全局安装与本地安装

npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有 -g 而已,比如:

1
2
npm install express          # 本地安装
npm install express -g   # 全局安装

如果出现以下错误:

npm err! Error: connect ECONNREFUSED 127.0.0.1:8087

解决办法为:

npm config set proxy null

本地安装

  1. 将安装包放在 node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
  2. 可以通过 require() 来引入本地安装的包。

全局安装

  1. 将安装包放在 /usr/local(Windows 下的 $HOME\AppData\Roaming\npm) 下或者你 node 的安装目录
  2. 可以直接在命令行里使用。

npm install -S/-D/-g 区别

主要区别就是依赖配置写入 package.json 文件的位置不同而已;

npm install 本身就有一个别名 npm i

-S –save

语法:` npm i module_name -S

npm install module_name --save 写入 dependencies,发布到生产环境

这样安装是局部安装的,会写进 package.json 文件中的 dependency 里。 dependencies: 表示生产环境下的依赖管理; 说白了你安装一个库如果是用来构建你的项目的,比如 echarts、element-ui,是实际在项目中起作用,就可以使用 -s 来安装

-D –save-dev

语法:npm i module_name -D

即 npm install module_name –save-dev 写入 devDependencies,发布到开发环境

这样安装是局部安装的,会写进 package.json 文件中的 devDependencies 里。 devDependencies :表示开发环境下的依赖管理 如果你安装的库是用来打包的、解析代码的,比如 webpack、babel,就可以用 -d 来安装,项目上线了,这些库就没用了

-g –global?

语法:npm i module_name -g

即 global 全局安装 (命令行使用)

npm install module_name -g,表示全局安装,安装一次过后,你就可在其他地方直接用啦

不加参数

npm i module_name 即 本地安装 (将安装包放在当前目录下的 node_modules 目录下)

npm5 开始通过 npm install module_name 什么都不加 和 npm install module_name –save 一样,都是局部安装并会把模块自动写入 package.json 中的 dependencies 里。

注意:-D,-S 分别是 –save-dev 和 –save 的简写,默认就是 -S,可以省略不写

查看安装信息 list

1
npm list -g # 查看所有全局安装的模块

6lehk

如果要查看某个模块的版本号,可以使用命令如下:npm list xxx

dhzx0

package.json 文件

package.json 位于模块的目录下,用于定义包的属性,以 express 模块的 package.json 来看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
{
  "name": "express",
  "description": "Fast, unopinionated, minimalist web framework",
  "version": "4.18.2",
  "author": "TJ Holowaychuk <tj@vision-media.ca>",
  "contributors": [
    "Aaron Heckmann <aaron.heckmann+github@gmail.com>",
    "Ciaran Jessup <ciaranj@gmail.com>",
    "Douglas Christopher Wilson <doug@somethingdoug.com>",
    "Guillermo Rauch <rauchg@gmail.com>",
    "Jonathan Ong <me@jongleberry.com>",
    "Roman Shtylman <shtylman+expressjs@gmail.com>",
    "Young Jae Sim <hanul@hanul.me>"
  ],
  "license": "MIT",
  "repository": "expressjs/express",
  "homepage": "http://expressjs.com/",
  "keywords": [
    "express",
    "framework",
    "sinatra",
    "web",
    "http",
    "rest",
    "restful",
    "router",
    "app",
    "api"
  ],
  "dependencies": {
    "accepts": "~1.3.8",
    "array-flatten": "1.1.1",
    "body-parser": "1.20.1",
    "content-disposition": "0.5.4",
    "content-type": "~1.0.4",
    "cookie": "0.5.0",
    "cookie-signature": "1.0.6",
    "debug": "2.6.9",
    "depd": "2.0.0",
    "encodeurl": "~1.0.2",
    "escape-html": "~1.0.3",
    "etag": "~1.8.1",
    "finalhandler": "1.2.0",
    "fresh": "0.5.2",
    "http-errors": "2.0.0",
    "merge-descriptors": "1.0.1",
    "methods": "~1.1.2",
    "on-finished": "2.4.1",
    "parseurl": "~1.3.3",
    "path-to-regexp": "0.1.7",
    "proxy-addr": "~2.0.7",
    "qs": "6.11.0",
    "range-parser": "~1.2.1",
    "safe-buffer": "5.2.1",
    "send": "0.18.0",
    "serve-static": "1.15.0",
    "setprototypeof": "1.2.0",
    "statuses": "2.0.1",
    "type-is": "~1.6.18",
    "utils-merge": "1.0.1",
    "vary": "~1.1.2"
  },
  "devDependencies": {
    "after": "0.8.2",
    "connect-redis": "3.4.2",
    "cookie-parser": "1.4.6",
    "cookie-session": "2.0.0",
    "ejs": "3.1.8",
    "eslint": "8.24.0",
    "express-session": "1.17.2",
    "hbs": "4.2.0",
    "marked": "0.7.0",
    "method-override": "3.0.0",
    "mocha": "10.0.0",
    "morgan": "1.10.0",
    "multiparty": "4.2.3",
    "nyc": "15.1.0",
    "pbkdf2-password": "1.2.1",
    "supertest": "6.3.0",
    "vhost": "~3.0.2"
  },
  "engines": {
    "node": ">= 0.10.0"
  },
  "files": [
    "LICENSE",
    "History.md",
    "Readme.md",
    "index.js",
    "lib/"
  ],
  "scripts": {
    "lint": "eslint .",
    "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
    "test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
    "test-cov": "nyc --reporter=html --reporter=text npm test",
    "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
  }
}

package.json 属性说明

  • name - 包名。
  • version - 包的版本号。
  • description - 包的描述。
  • homepage - 包的官网 url 。
  • author - 包的作者姓名。
  • contributors - 包的其他贡献者姓名。
  • dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
  • devDependencies 开发阶段的依赖,不打包进去
  • repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
  • main - main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js
  • keywords - 关键字

scripts

可以通过配置 package.json 中的 scripts 属性来启动项目

1
2
3
4
5
6
{
    "scripts": {
        "server": "node server.js",
        "start": "node index.js",
    },
}

配置完成之后,执行命令启动项目:

npm run server npm run start

不过 start 比较特别,使用时可以省略 run

npm start

npm run 有自动向上级目录查找的特性,跟 require 函数一样

devDependencies 与 dependencies 有什么区别呢?

  • devDependencies 表示 开发环境 下的依赖管理,里面的插件只用于开发环境(开发时依赖),不用于生产环境,例如 vite、sass 插件等 (打包后就跟他没关系了);
  • dependencies 表示 生产环境 下的依赖管理,里面的插件只用于生产环境(运行时依赖),是需要发布到生产环境的,例如 vue、element-plus 等等。

这种说法不能说完全错误,但至少是不够清晰的,我们很难因此真正理解它们,所以就会在日常工作中经常踩依赖包版本的坑。

甚至有一种精简化后更广为流传的说法:dependencies = 生产依赖,devDependencies = 开发依赖,更是对我们产生了误导。

开发 Web 应用 时,即使将所有依赖声明在 devDependencies 中,也不会影响应用的成功构建、打包与运行。

因此 dependencies = 生产依赖,devDependencies = 开发依赖 的说法是片面的。 我们常说的 ” 生产环境 “、” 开发环境 “ 是构建时行为,构建并不是包管理器的职责,而是 webpackrollupvite 的工具的工作,此时包管理器起的作用仅仅是执行脚本而已。 各种包管理器处理 dependencies 和 devDependencies 差异的行为都发生在依赖安装时期,即 npm install 的过程中。


包管理器将以项目的 package.json 为起点,安装所有 dependencies 与 devDependencies 中声明的依赖。但是对于这些一级依赖项具有的更深层级依赖,在深度遍历的过程中,只会安装 dependencies 中的依赖,忽略 devDependencies 中的依赖。

为什么会这样呢?因为包管理器认为:作为包的使用者,我们当然不用再去关心它们开发构建时的依赖,所以会为我们忽略 devDependencies。 而 dependencies 是包产物正常工作所依赖的内容,当然有必要安装。

回到 Web 应用 开发的场景,Web 应用 的产物往往部署到服务器,不会发布到 npm 仓库供其他用户使用,而包管理器对于一级依赖,无论 dependencies 还是 devDependencies 都会悉数安装。这种情况下, dependencies 与 devDependencies 可能真的只有语义化约定的作用了。

卸载模块 uninstall

npm uninstall express

卸载后,你可以到 /node_modules/ 目录下查看包是否还存在,或者使用以下命令查看:

npm ls

更新模块 update

$ npm update express

$ npm search express

创建模块 init

创建模块,package.json 文件是必不可少的。我们可以使用 NPM 生成 package.json 文件,生成的文件包含了基本的结果。

  • 按照提示来输入基本信息,在最后输入 “yes” 后会生成 package.json 文件。
1
npm init
  • 在 npm 资源库中注册用户(使用邮箱注册):
1
npm adduser
  • 发布模块
1
npm publish

版本号

NPM 使用 语义版本号 来管理代码:语义版本号分为 X.Y.Z 三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。

  • 如果只是修复 bug,需要更新 Z 位。
  • 如果是新增了功能,但是向下兼容,需要更新 Y 位。
  • 如果有大变动,向下不兼容,需要更新 X 位。

版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如 "argv": "0.0.x" 表示依赖于 0.0.x 系列的最新版 argv。

NPM 支持的所有版本号范围指定方式可以查看官方文档。

NPM 常用命令

  • NPM 提供了很多命令,例如 install 和 publish,使用 npm help 可查看所有命令。
  • 使用 npm help <command> 可查看某条命令的详细帮助,例如 npm help install。
  • 在 package.json 所在目录下使用 npm install . -g 可先在本地安装当前命令行程序,可用于发布前的本地测试。
  • 使用 npm update <package> 可以把当前目录下 node_modules 子目录里边的对应模块更新至最新版本。
  • 使用 npm update <package> -g 可以把全局安装的对应命令行程序更新至最新版。
  • 使用 npm cache clear 可以清空 NPM 本地缓存,用于对付使用相同版本号发布新版本代码的人。
  • 使用 npm unpublish <package>@<version> 可以撤销发布自己发布过的某个版本代码。

npx

npm 从 5.25.2 版开始,增加了 npx 命令。

npx 的作用

  • npm 只能管理包的依赖,npx 则可以快捷的运用包中的命令行工具和其他可执行文件,让项目内部安装的模块用起来更方便。
  • 当执行 npx <command> 相关命令的时候,npx 会先本地找(可以是项目中的也可以是本机的)寻找这个 command
    • 找到了:就用本地的版本
    • 没找到:直接下载最新版本(这里是在缓存里),完成命令要求
    • 使用完之后就会完全清除,不会在本机或项目留下任何东西
    • 这样就不会污染本机、永远使用最新版本的 dependency

.npmrc

什么是.npmrc

.npmrc,可以理解成 npm running configuration, 即 npm 运行时配置文件。可以在.npmrc 中进行配置:从哪里下载、下载到电脑什么路径等。不止存在一个.npmrc 文件,而是有多个。在我们安装包的时候,npm 按照如下顺序读取这些配置文件:

.npmrc 位置

  • 项目配置文件 (/project/.npmrc, Windows: "C:\\Users\\Administrator\\AppData\\Roaming\\npm\\etc\\npmrc"):你可以在项目的根目录下创建一个.npmrc 文件,只用于管理这个项目的 npm 安装。
  • 用户配置文件 (~/.npmrc):在你使用一个账号登陆的电脑的时候,可以为当前用户创建一个.npmrc 文件,之后用该用户登录电脑,就可以使用该配置文件。可以通过 npm config get userconfig 来获取该文件的位置。
  • 全局配置文件 ($PREFIX/etc/npmrc): 一台电脑可能有多个用户,在这些用户之上,你可以设置一个公共的.npmrc 文件,供所有用户使用。该文件的路径为: $PREFIX/etc/npmrc,使用 npm config get prefix 获取 $ PREFIX 。如果你不曾配置过全局文件,该文件不存在。
  • npm 内嵌配置文件 (/path/to/npm/npmrc):最后还有 npm 内置配置文件,基本上用不到

如何设置.npmrc?

1、设置项目配置文件 在项目的根目录下新建 .npmrc 文件,在里面以 key=value 的格式进行配置。比如要把 npm 的源配置为淘宝源,可以参考一下代码:

1
registry=https://registry.npm.taobao.org

2、设置用户配置文件 直接通过 npm config get userconfig 命令找到该文件的路径,然后直接仿照上述方法该文件,也可以通过 npm config set 命令继续设置,命令如下:

1
2
config set registry https://registry.npm.taobao.org
# 最终,命令行会帮助我们修改对应的配置文件。只不过使用命令行更加快捷。

删除,直接编辑 .npmrc 删除,或只用命令:

1
npm config delete registry

3、设置全局配置文件 方法和设置用户配置文件如出一辙,只不过在使用命令行时需要加上 -g 参数。

1
npm config set registry https://registry.npm.taobao.org -g

常用的 npm 设置命令

1
2
3
4
5
6
7
npm config set <key> <value> [-g|--global]  //给配置参数key设置值为value;
npm config get <key>          //获取配置参数key的值;
npm config delete <key>       //删除置参数key及其值;
npm config list [-l]      //显示npm的所有配置参数的信息;
npm config edit     //编辑配置文件
npm get <key>     //获取配置参数key的值;
npm set <key> <value> [-g|--global]    //给配置参数key设置值为value;

.npmrc 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ===============================
# npm 用户全局配置 —— happy coder 等命令全局可用
# ===============================

# 全局安装目录(避免 sudo 权限问题)
prefix=/Users/xxx/.npm-global

# 国内高速源(原 npm 官方源网络不稳定)
registry=https://registry.npmmirror.com

# 启用严格 SSL 校验(更安全)
strict-ssl=true

# 安装依赖时锁定精确版本
save-exact=true

# 设置缓存路径(可选)
cache=/Users/xxx/.npm-global/cache

# 日志级别(info / warn / error)
loglevel=info

# 不显示进度条(可选,节省输出)
progress=false

问题

npm install 运行卡住不动

npm config set registry http://registry.cnpmjs.org

ucw6b或者用淘宝 npm 镜像

npm root 权限问题

在 Mac 上全局 (-g) 安装包时,需要 root 权限,用下面 shell 脚本可修复,mac_npm_fix.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/bin/bash
# ===============================
# Mac NPM 权限修复 + 无 sudo 全局安装环境配置
# ===============================
# 目的:
# 1. 解决 npm 安装全局包需要 sudo 权限的问题
# 2. 配置一个自己的全局安装目录 ~/.npm-global
# 3. 确保 shell 能找到这些全局包的命令
# ===============================

set -e  # 遇到错误立即退出(更安全)

echo "📦 1. 检测当前用户和组..."
USER_NAME=$(whoami)         # 当前登录用户
GROUP_NAME=$(id -gn)        # 当前用户组
echo "当前用户: $USER_NAME"
echo "当前用户组: $GROUP_NAME"

echo "🔧 2. 修复 npm 缓存目录权限..."
# npm 在执行时会缓存一些文件到 ~/.npm,如果权限不对会导致安装失败
if [ -d "$HOME/.npm" ]; then
    sudo chown -R "$USER_NAME":"$GROUP_NAME" "$HOME/.npm"
    echo "✅ 已修复 ~/.npm 权限"
else
    echo "ℹ ~/.npm 目录不存在,跳过"
fi

echo "🔧 3. 修复全局安装目录权限..."
# ~/.npm-global 是我们自定义的全局安装目录,避免写到 /usr/local 需要 sudo
if [ -d "$HOME/.npm-global" ]; then
    sudo chown -R "$USER_NAME":"$GROUP_NAME" "$HOME/.npm-global"
    echo "✅ 已修复 ~/.npm-global 权限"
else
    echo "📁 创建 ~/.npm-global 目录"
    mkdir -p "$HOME/.npm-global"
    sudo chown -R "$USER_NAME":"$GROUP_NAME" "$HOME/.npm-global"
fi

echo "⚙️ 4. 设置 npm 全局安装目录为 ~/.npm-global ..."
# -----------------------------
# 第4步:只影响 npm 的安装位置
# 意思是以后 npm install -g xxx 会把包放到 ~/.npm-global/bin(可执行文件) 和 ~/.npm-global/lib(包文件)
# -----------------------------
npm config set prefix "$HOME/.npm-global"

# 额外:写入到用户级配置文件 ~/.npmrc,保证长期生效
grep -q "prefix=" "$HOME/.npmrc" || echo "prefix=$HOME/.npm-global" >> "$HOME/.npmrc"

echo "🌍 5. 将 ~/.npm-global/bin 添加到 PATH ..."
# -----------------------------
# 第5步:影响 shell 找命令的路径
# npm 安装后,可执行文件在 ~/.npm-global/bin,如果 PATH 里没有这个目录,执行命令会提示 not found
# 所以必须把这个目录加到 PATH,这样比如输入 happy 就能找到 ~/.npm-global/bin/happy
# -----------------------------
for rcfile in "$HOME/.zshrc" "$HOME/.bashrc"; do
    if [ -f "$rcfile" ]; then
        grep -q ".npm-global/bin" "$rcfile" || echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> "$rcfile"
    fi
done

echo "➡️ 6. 请运行: source ~/.zshrc 或重新打开终端以使 PATH 生效"

echo "🚀 7. 完成!现在你可以无 sudo 全局安装 npm 包了"
echo "例如: npm i -g @anthropic-ai/claude-code happy-coder"

其他 js 包管理器

见 [[其他 js 包管理器]]

Ref

本文由作者按照 CC BY 4.0 进行授权