└── README.md /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # npm 基本用法和实用技巧 4 | 5 | 6 | 7 | - [基本用法](#基本用法) 8 | - [安装与升级](#安装与升级) 9 | - [安装](#安装) 10 | - [升级](#升级) 11 | - [安装指定版本的 npm](#安装指定版本的-npm) 12 | - [常用命令](#常用命令) 13 | - [npm install](#npm-install) 14 | - [npm uninstall](#npm-uninstall) 15 | - [npm update](#npm-update) 16 | - [npm ls](#npm-ls) 17 | - [npm adduser](#npm-adduser) 18 | - [npm init](#npm-init) 19 | - [npm publish](#npm-publish) 20 | - [npm unpublish](#npm-unpublish) 21 | - [npm deprecate](#npm-deprecate) 22 | - [npm dist-tag](#npm-dist-tag) 23 | - [npm view](#npm-view) 24 | - [npm link](#npm-link) 25 | - [npm conifg](#npm-conifg) 26 | - [工作原理](#工作原理) 27 | - [实用技巧](#实用技巧) 28 | - [pacakge.json](#pacakgejson) 29 | - [dependencies](#dependencies) 30 | - [optionalDependencies](#optionaldependencies) 31 | - [peerDependencies](#peerdependencies) 32 | - [bundledDependencies](#bundleddependencies) 33 | - [bin](#bin) 34 | - [config](#config) 35 | - [.npmrc](#npmrc) 36 | - [.npmignore](#npmignore) 37 | - [scripts](#scripts) 38 | - [shrinkwrap](#shrinkwrap) 39 | - [cache](#cache) 40 | 41 | 42 | 43 | ## 基本用法 44 | 45 | ### 安装与升级 46 | 47 | #### 安装 48 | 49 | 安装 Node.js 时会自动安装 npm。 50 | 51 | ``` 52 | nvm install 4 53 | ``` 54 | 55 | #### 升级 56 | 57 | ``` 58 | npm install npm -g 59 | ``` 60 | 61 | #### 安装指定版本的 npm 62 | 63 | ``` 64 | npm install npm@2 -g 65 | ``` 66 | 67 | ### 常用命令 68 | 69 | #### npm install 70 | 71 | 1. 根据 `package.json` 文件安装依赖。 72 | 73 | ``` 74 | npm install 75 | ``` 76 | 77 | 2. 安装指定的依赖包。 78 | 79 | ``` 80 | npm install [<@scope>/] 81 | ``` 82 | 83 | > 如果当前目录中存在 `package.json` 文件,则安装满足文件中版本规则的最高版本;否则安装最新版本依赖包。 84 | 85 | 3. 安装指定版本的依赖包。 86 | 87 | ``` 88 | npm install [<@scope>/]@ 89 | npm install [<@scope>/]@ 90 | npm install [<@scope>/]@ 91 | ``` 92 | 93 | 4. 从本地位置安装依赖。 94 | 95 | ``` 96 | npm install 97 | npm install 98 | ``` 99 | 100 | > 可以用 `npm pack` 生成 `` 101 | 102 | 5. 从网络位置安装依赖。 103 | 104 | ``` 105 | npm install 106 | npm install 107 | npm install / 108 | ``` 109 | 110 | 6. 常用参数: 111 | 112 | - `-g, --global`:安装全局依赖,如果没有指定依赖包名,则将当前目录中的包安装至全局 113 | 114 | - `-S, --save`:安装依赖的同时将该依赖写入 `dependencies` 115 | - `-D, --save-dev`:安装依赖的同时将该依赖写入 `devDependencies` 116 | - `-O, --save-optional`:安装依赖的同时将该依赖写入 `optionalDependencies` 117 | - `-E, --save-exact`:写入 `package.json` 时带有确切版本号 118 | 119 | - `--no-optional`:不安装 optional dependencies,可继承 120 | - `--only={dev[elopment]|prod[uction]}`:无视 `NODE_ENV`,只安装 `devDependencies` 或仅安装除了 `devDependencies` 之外的依赖项 121 | - `--dry-run`:走一遍安装的过程并报告结果,但实际上没有安装任何依赖 122 | 123 | 7. 别名:`i` 124 | 125 | #### npm uninstall 126 | 127 | 1. 删除一个指定的依赖包,并且完全移除为了该包而安装的任何文件 128 | 129 | ``` 130 | npm uninstall [<@scope>/][@]... [-S|--save|-D|--save-dev|-O|--save-optional] 131 | ``` 132 | 133 | 2. 常用参数:与 `npm install` 类似 134 | 135 | 3. 别名:`remove`、`rm`、`r`、`un`、`unlink` 136 | 137 | #### npm update 138 | 139 | 1. 升级所有依赖包至版本规则允许的最新版本,并安装缺失的依赖包 140 | 141 | ``` 142 | npm update [...] 143 | ``` 144 | 145 | 2. 常用参数: 146 | 147 | - `-g`:升级全局依赖包 148 | - `--dev`:同时升级在 `devDependencies` 中的依赖包 149 | - `--depth Infinity`:从 `npm@2.6.1` 起 `npm update` 默认仅升级顶层依赖,使用该参数升级所有依赖包 150 | - `--save`:升级依赖包,同时记录升级后的版本 151 | 152 | 3. 别名:`up`、`upgrade` 153 | 154 | #### npm ls 155 | 156 | 1. 以树形结构打印依赖包及其版本 157 | 158 | ``` 159 | npm ls [[<@scope>/] ...] 160 | ``` 161 | 162 | 2. 常用参数: 163 | 164 | - `--json`:以 JSON 格式输出 165 | - `--long`:输出额外信息 166 | - `--global`:输出全局依赖信息 167 | - `--depth `:输出依赖树的最大深度 168 | - `--prod[uction]`:仅输出 `dependencies` 中的依赖 169 | - `--dev`:仅输出 `devDependencies` 中的依赖 170 | 171 | 3. 别名:`list`、`la`、`ll` 172 | 173 | #### npm adduser 174 | 175 | 登录 npm 176 | 177 | ``` 178 | npm adduser 179 | ``` 180 | 181 | #### npm init 182 | 183 | 1. 提问,然后产生一个 `package.json` 文件 184 | 185 | ``` 186 | npm init [-f|--force|-y|--yes] 187 | ``` 188 | 189 | 2. 常用参数: 190 | 191 | - `-f, --force, -y, --yes`:使用默认的答案,不再提问 192 | - `--scope `:指定新模块的 scope,例如 `mtfe` 193 | 194 | #### npm publish 195 | 196 | 1. 发布一个新的包,或一个包的新版本 197 | 198 | ``` 199 | npm publish [|] [--tag ] [--access ] 200 | ``` 201 | 202 | > 如果没有 tarball 或 folder 被指定,则使用当前目录 203 | 204 | 2. 常用参数: 205 | 206 | - `--tag `:给被发布的包注册指定的 tag,如果没有该参数,则默认使用 `latest` 207 | 208 | #### npm unpublish 209 | 210 | 取消发布一个包,或一个包的某些版本 211 | 212 | ``` 213 | npm unpublish [<@scope>/][@] 214 | ``` 215 | 216 | #### npm deprecate 217 | 218 | 弃用一个包,或一个包的某些版本,尝试安装这些弃用包的用户将会收到警告 219 | 220 | ``` 221 | npm deprecate [@] 222 | ``` 223 | 224 | #### npm dist-tag 225 | 226 | 1. 给一个包的某个版本注册 tag 227 | 228 | ``` 229 | npm dist-tag add @ [] 230 | ``` 231 | 232 | 2. 移除一个 tag 233 | 234 | ``` 235 | npm dist-tag rm 236 | ``` 237 | 238 | 3. 显示指定包的所有 tag 239 | 240 | ``` 241 | npm dist-tag ls [] 242 | ``` 243 | 244 | #### npm view 245 | 246 | 1. 显示一个包的详细信息 247 | 248 | ``` 249 | npm view [<@scope>/][@] [[.]...] 250 | ``` 251 | 252 | > `` 和 `` 表示输出信息中的字段 253 | 254 | 2. 别名:`info`、`show`、`v` 255 | 256 | #### npm link 257 | 258 | 将一个本地目录中的模块符号链接至一个项目的依赖中,实现上述功能需要两步: 259 | 260 | 1. 在模块目录中执行下面的命令,创建一个从全局依赖指向当前目录的符号链接 261 | 262 | ``` 263 | npm link 264 | ``` 265 | 266 | ``` 267 | /usr/local/Cellar/nvm/0.25.4/versions/node/v4.4.4/lib/node_modules/handgrip 268 | -> /Users/Dylan/handgrip 269 | ``` 270 | 271 | 2. 在其他目录中执行下面的命令,创建一个从局部依赖指向全局依赖的符号链接 272 | 273 | ``` 274 | npm link [<@scope>/][@] 275 | ``` 276 | 277 | > `[<@scope>/][@]` 所表示已经执行了第一步的模块,或其所包含的版本 278 | 279 | ``` 280 | # npm link handgrip 281 | /Users/Dylan/koalition-boilerplate/node_modules/handgrip 282 | -> /usr/local/Cellar/nvm/0.25.4/versions/node/v4.4.4/lib/node_modules/handgrip 283 | -> /Users/Dylan/handgrip 284 | ``` 285 | 286 | 由于依赖通过符号链接的方式组织,在模块目录中的修改可以立即在其他目录中生效。 287 | 288 | #### npm conifg 289 | 290 | 1. 设置一个配置项 291 | 292 | ``` 293 | npm config set [-g|--global] 294 | npm set [-g|--global] 295 | ``` 296 | 297 | 如果配置项的值阙如,将采用默认值 `true`。 298 | 299 | 2. 读取一个配置项 300 | 301 | ``` 302 | npm config get 303 | npm get 304 | ``` 305 | 306 | 3. 删除一个配置项 307 | 308 | ``` 309 | npm config delete key 310 | ``` 311 | 312 | 4. 列出所有的配置 313 | 314 | ``` 315 | npm config list 316 | ``` 317 | 318 | 5. 在编辑器中打开配置文件 319 | 320 | ``` 321 | npm config edit 322 | ``` 323 | 324 | 使用 `--global` 来打开全局配置文件。 325 | 326 | ## 工作原理 327 | 328 | > 下面的内容基本上翻译了 [npm v3 Dependency Resolution](https://docs.npmjs.com/how-npm-works/npm3)、[npm3 Duplication and Deduplication](https://docs.npmjs.com/how-npm-works/npm3-dupe)、[npm3 Non-determinism](https://docs.npmjs.com/how-npm-works/npm3-nondet) 这三篇文章 329 | 330 | npm v3 依赖解析的主要思想:尽可能地减少间接依赖安装目录的深度,最理想的情况是与直接依赖安装在同一目录下,通过这种方式来减少依赖目录的嵌套,缓解整个依赖目录层次过深的问题。(因为 Windows 中文件路径的长度不能大于 260 个字符。) 331 | 332 | 🌰: 333 | 334 | 假如我们有模块 A,模块 A 依赖了模块 B。 335 | 336 | ![模块 A 依赖模块 B](https://docs.npmjs.com/images/npm3deps1.png) 337 | 338 | 然后我们创建了已依赖模块 A 的应用 App。 339 | 340 | 在执行 `npm install` 的时候,npm v3 会把模块 A 及其依赖模块 B 都安装在 `/node_modules` 目录中。(npm v2 则会把模块 B 会嵌套在模块 A 中。) 341 | 342 | ![npm2 vs 3](https://docs.npmjs.com/images/npm3deps2.png) 343 | 344 | 此时,我们的应用 App 又需要依赖模块 C,而模块 C 依赖了另一个版本的模块 B。 345 | 346 | ![新依赖模块 C](https://docs.npmjs.com/images/npm3deps3.png) 347 | 348 | 然而,由于模块 B v1.0 已经被安装在了顶层依赖目录中,模块 B v2.0 就无法安装到同一位置了。这种情况下,npm v3 将会默认采用 npm v2 的行为,将这个新的模块 B 嵌套在依赖它的模块中,也就是说,把模块 B v2.0 安装到模块 C 中。 349 | 350 | ![依赖嵌套](https://docs.npmjs.com/images/npm3deps4.png) 351 | 352 | 在控制台打印出依赖树和目录树。 353 | 354 | ![依赖树与目录树](https://docs.npmjs.com/images/tree.png) 355 | 356 | *** 357 | 358 | *如果再安装一个依赖模块 B v1.0 或 v2.0 会怎么样呢?* 359 | 360 | 此时,我们的应用 App 又需要依赖模块 D,而模块 D 与模块 C 一样,依赖了模块 B v2.0。 361 | 362 | ![新依赖模块 D](https://docs.npmjs.com/images/npm3deps5.png) 363 | 364 | 由于模块 B v1.0 已经被安装在了顶层依赖目录中,模块 B v2.0 就无法安装到同一位置了。因此,尽管模块 C 中已经有了一份拷贝,模块 B v2.0 还是被安装到了模块 D 中(否则就 requrie 不到了)。 365 | 366 | ![未去重](https://docs.npmjs.com/images/npm3deps6.png) 367 | 368 | 如果一个间接依赖被两个以上的包所依赖,且不能安装在顶层依赖目录中,那么这个间接依赖会被复制一份,并嵌套在直接依赖的目录中。 369 | 370 | 相反地,如果一个间接依赖被两个以上的包所依赖,且被安装在顶层依赖目录中,那么这个依赖就不会被复制,而被依赖它的包所共享。 371 | 372 | 🌰,我们又加了一个依赖模块 E,模块 E 与模块 A 一样,依赖了模块 B v1.0。 373 | 374 | ![新依赖模块 E](https://docs.npmjs.com/images/npm3deps7.png) 375 | 376 | 由于模块 B v1.0 已经安装再来顶层依赖目录中,所以不需要复制并嵌套该模块,直接安装模块 E,然后模块 E 就可以与模块 A 共享模块 B v1.0 了。 377 | 378 | ![](https://docs.npmjs.com/images/npm3deps8.png) 379 | 380 | 在控制台打印出依赖树和目录树。 381 | 382 | ![依赖树与目录树](https://docs.npmjs.com/images/tree2.png) 383 | 384 | *** 385 | 386 | *如果我们把模块 A 升级至 v2.0,模块 A v2.0 不再依赖模块 B v1.0,而是依赖模块 B v2.0,这会怎么样呢?* 387 | 388 | 执行 `npm install mod-a@2`,npm v3 将做以下事情: 389 | 390 | 1. 移除模块 A v1.0 391 | 2. 安装模块 A v2.0 392 | 3. 保留模块 B v1.0,因为模块 E 仍旧依赖它 393 | 4. 由于模块 B v1.0 还在顶层依赖目录中,模块 B v2.0 被嵌套安装在模块 A v2.0 中 394 | 395 | ![](https://docs.npmjs.com/images/npm3deps10.png) 396 | 397 | 在控制台打印出依赖树和目录树。 398 | 399 | ![依赖树与目录树](https://docs.npmjs.com/images/tree3.png) 400 | 401 | 最后,我们吧模块 E 升级至 v2.0,模块 E v2.0 同样不再依赖模块 B v1.0,而是依赖模块 B v2.0。 402 | 403 | ![](https://docs.npmjs.com/images/npm3deps11.png) 404 | 405 | npm v3 将做以下事情: 406 | 407 | 1. 移除模块 E v1.0 408 | 2. 安装模块 E v2.0 409 | 3. 由于没有模块依赖模块 B v1.0,移除该模块 410 | 4. 由于顶层依赖目录中没有模块 B,在该目录中安装模块 B v2.0 411 | 412 | ![](https://docs.npmjs.com/images/npm3deps12.png) 413 | 414 | 在控制台打印出依赖树和目录树。 415 | 416 | ![依赖树与目录树](https://docs.npmjs.com/images/tree4.png) 417 | 418 | 现在,模块 B v2.0 几乎出现在了每一个依赖目录中,这显然不够简洁,我们可以执行: 419 | 420 | ``` 421 | npm dedupe 422 | ``` 423 | 424 | 这条命令可以仅保留顶层依赖目录中的模块 B v2.0,而移除其他次级目录中的拷贝。 425 | 426 | 在控制台打印出目录树。 427 | 428 | ![目录树](https://docs.npmjs.com/images/tree5.png) 429 | 430 | *** 431 | 432 | 我们让 App 回到刚才的一个状态: 433 | 434 | ![](https://docs.npmjs.com/images/npm3deps8.png) 435 | 436 | 在这个🌰中,我们的应用有以下 `package.json` 文件: 437 | 438 | ```js 439 | { 440 |   "name": "example3", 441 |   "version": "1.0.0", 442 |   "description": "", 443 |   "main": "index.js", 444 |   "scripts": { 445 |     "test": "echo \"Error: no test specified\" && exit 1" 446 |   }, 447 |   "keywords": [], 448 |   "author": "", 449 |   "license": "ISC", 450 |   "dependencies": { 451 |     "mod-a": "^1.0.0", 452 |     "mod-c": "^1.0.0", 453 |     "mod-d": "^1.0.0", 454 |     "mod-e": "^1.0.0" 455 |   } 456 | } 457 | ``` 458 | 459 | 如果此时运行 `npm install`,控制台会输出以下结果: 460 | 461 | ![](https://docs.npmjs.com/images/npm3deps14.png) 462 | 463 | 然后,我们再次把模块 A 升级到 v2.0,模块 A v2.0 不在依赖模块 B v1.0,而是依赖模块 B v2.0: 464 | 465 | ``` 466 | npm install mod-a@2 --save 467 | ``` 468 | 469 | 控制台输出: 470 | 471 | ![](https://docs.npmjs.com/images/npm3deps15.png) 472 | 473 | 此时,我们的依赖目录看起来是这样的: 474 | 475 | ![](https://docs.npmjs.com/images/npm3deps10.png) 476 | 477 | 而且我们得到了一个新的 `package.json`: 478 | 479 | ```js 480 | { 481 |   "name": "example3", 482 |   "version": "1.0.0", 483 |   "description": "", 484 |   "main": "index.js", 485 |   "scripts": { 486 |     "test": "echo \"Error: no test specified\" && exit 1" 487 |   }, 488 |   "keywords": [], 489 |   "author": "", 490 |   "license": "ISC", 491 |   "dependencies": { 492 |     "mod-a": "^2.0.0", 493 |     "mod-c": "^1.0.0", 494 |     "mod-d": "^1.0.0", 495 |     "mod-e": "^1.0.0" 496 |   } 497 | } 498 | ``` 499 | 500 | 接着,我们运行: 501 | 502 | ``` 503 | npm install 504 | ``` 505 | 506 | 控制台输出: 507 | 508 | ![](https://docs.npmjs.com/images/npm3deps16.png) 509 | 510 | 依赖目录变成了: 511 | 512 | ![](https://docs.npmjs.com/images/npm3deps17.png) 513 | 514 | *** 515 | 516 | **总结:** 517 | 518 | 1. 依赖目录的结构取决于依赖安装的顺序 519 | 520 | ![](https://docs.npmjs.com/images/install-order.png) 521 | 522 | 2. `npm install` (不带参数)安装出的依赖目录结构是稳定的,因为 `package.json` 中依赖的排列顺序总是字典序 523 | 3. npm v3 需要尽可能的减少间接依赖安装目录的深度,于是不得不从树根至树叶一级一级遍历下来,寻找可用的最远祖先节点,严重延长了依赖的安装时间 524 | 525 | ## 实用技巧 526 | 527 | ### pacakge.json 528 | 529 | #### dependencies 530 | 531 | 版本号的写法: 532 | 533 | - `version`: 必须匹配确切的版本号 534 | - `>version`、`>=version`、`=1.2.3 <1.3.0` 537 | - `~1.2` := `>=1.2.0 <1.3.0` 538 | - `~1` := `>=1.0.0 <2.0.0` 539 | - `~0.2.3` := `>=0.2.3 <0.3.0` 540 | - `~0` := `>=0.0.0 <1.0.0` 541 | - `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0` 542 | - `^version`:允许版本号中不修改最左非零位及其前缀的所有版本号更高的变化 543 | - `^1.2.3` := `>=1.2.3 <2.0.0` 544 | - `^0.2.3` := `>=0.2.3 <0.3.0` 545 | - `^0.0.3` := `>=0.0.3 <0.0.4` 546 | - `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0` 547 | - `^0.0.3-beta` := `>=0.0.3-beta <0.0.4` 548 | - `^1.2.x` := `>=1.2.0 <2.0.0` 549 | - `^0.0.x` := `>=0.0.0 <0.1.0` 550 | - `^0.0` := `>=0.0.0 <0.1.0` 551 | - `^1.x` := `>=1.0.0 <2.0.0` 552 | - `1.2.x`:`1.2.0`、`1.2.1` 等,不包括 `1.3.0` 553 | - `*` 或空:任何版本 554 | - `version1 - version2`:`>=version1 <=version2` 555 | - `range1 || range2` 556 | - `tag`:指定 tag 557 | - `file:...`:本地路径 558 | - `http://...`、`git...`:网络路径 559 | - `git://github.com/user/project.git#commit-ish` 560 | - `git+ssh://user@hostname:project.git#commit-ish` 561 | - `git+ssh://user@hostname/project.git#commit-ish` 562 | - `git+http://user@hostname/project/blah.git#commit-ish` 563 | - `git+https://user@hostname/project/blah.git#commit-ish` 564 | - `user/repo#commit-ish`:GitHub 仓库 565 | 566 | #### optionalDependencies 567 | 568 | `optionalDependencies` 中的依赖安装失败时,npm 不会停止整个安装过程。 569 | 570 | 模块本身应当处理由于依赖安装失败导致依赖缺失的问题,🌰如: 571 | 572 | ```js 573 | try { 574 | var foo = require('foo') 575 | var fooVersion = require('foo/package.json').version 576 | } catch (er) { 577 | foo = null 578 | } 579 | if ( notGoodFooVersion(fooVersion) ) { 580 | foo = null 581 | } 582 | 583 | // .. then later in your program .. 584 | 585 | if (foo) { 586 | foo.doFooThings() 587 | } 588 | ``` 589 | 590 | #### peerDependencies 591 | 592 | `peerDependencies` 表示当前模块**适配**其他某些模块,也就是只有当那些指定的模块被安装时,当前模块才会被安装。 593 | 594 | 🌰如: 595 | 596 | ```js 597 | { 598 | "name": "tea-latte", 599 | "version": "1.3.5", 600 | "peerDependencies": { 601 | "tea": "2.x" 602 | } 603 | } 604 | ``` 605 | 606 | 表示确保模块 tea-latte 只能同模块 tea 2.x 一起安装。 607 | 608 | > 如果 `peerDependencies` 中的模块没有被明确依赖的话,npm v2 会自动安装这些模块,但 npm v3 不会再安装这些模块,而是输出一个警告。 609 | 610 | #### bundledDependencies 611 | 612 | `bundledDependencies` 表示在当前模块打包或发布时,需要被置于模块内部的依赖。 613 | 614 | 使用的时机: 615 | 616 | 1. 使用的依赖不是来自于 npm,或者修改了这个依赖 617 | 2. 使用开发者自己的项目作为依赖 618 | 3. 希望在模块中包含一些文件 619 | 620 | #### bin 621 | 622 | 将可执行的文件安装到 PATH 中。可能安装到的位置有: 623 | 624 | ``` 625 | # global: 626 | /usr/local/opt/nvm/versions/node/v4.4.4/bin/ 627 | /usr/local/bin/ 628 | 629 | # local: 630 | ./node_modules/.bin/ 631 | ``` 632 | 633 | 🌰: 634 | 635 | ```js 636 | { "bin" : { "myapp" : "./cli.js" } } 637 | ``` 638 | 639 | 当安装这个模块时,npm 会创建一个指向 `cli.js` 的符号链接。 640 | 641 | ```js 642 | { "name": "my-program", 643 | "version": "1.2.5", 644 | "bin": "./path/to/program" } 645 | ``` 646 | 647 | 上面的写法等价于: 648 | 649 | ```js 650 | { "name": "my-program", 651 | "version": "1.2.5", 652 | "bin" : { "my-program" : "./path/to/program" } } 653 | ``` 654 | 655 | #### config 656 | 657 | `package.json` 文件中的 `config` 字段可以用来设置模块脚本中可以用到的配置参数。 658 | 659 | 🌰,如果一个模块有: 660 | 661 | ```js 662 | { "name" : "foo", 663 | "config" : { "port" : "8080" } } 664 | ``` 665 | 666 | 那么在模块脚本(如 `start`)中就可以通过 `process.env.npm_package_config_port`,访问到这个配置。 667 | 668 | 这个配置也可以被命令 `npm config set foo:port 8001` 覆盖。 669 | 670 | ### .npmrc 671 | 672 | 配置文件有: 673 | 674 | - 项目配置文件(/path/to/my/project/.npmrc) 675 | - 用户配置文件(~/.npmrc) 676 | - 全局配置文件(/path/to/node/etc/npmrc) 677 | - 内置配置文件(/path/to/npm/npmrc) 678 | 679 | 环境变量用法: 680 | 681 | ``` 682 | prefix = ${HOME}/.npm-packages 683 | ``` 684 | 685 | 数组用法: 686 | 687 | ``` 688 | key[] = "first value" 689 | key[] = "second value" 690 | ``` 691 | 692 | > 项目配置文件和用户配置文件的权限必须设置为只能被当前用户读写(0600),否则该配置文件会被 npm 忽略 693 | 694 | 可以通过 `npm config` 命令来管理配置文件。 695 | 696 | 常用配置项: 697 | 698 | - `cache`:npm 本地缓存目录,默认 `~/.npm` 699 | - `cache-max`:保持缓存项目且不向 registry 检查的最长时间,单位秒,默认 `Infinity`,缓存中的数据不会自动删除除非执行 `npm cache clean` 命令 700 | - `cache-min`:保持缓存项目且不向 registry 检查的最短时间,单位秒,默认 `10`,可以置为 `999999` 等以尽量延长缓存生效时间 701 | - `depth`:`npm ls` 等命令中的默认深度,默认 `Infinity` 702 | - `editor`:npm 默认使用的编辑器 703 | - `engine-strict`:如果置为 `true`,npm 将会拒绝安装不符合当前 Node.js 版本的模块 704 | - `force`:强力执行一些命令 705 | - 生命周期脚本执行失败不再阻塞安装过程 706 | - 发布会覆盖已经发布的版本 707 | - 访问 registry 时会跳过缓存 708 | - `global`:全局模式 709 | - `globalconfig`:全局配置文件的路径 710 | - `global-style`:以安装全局依赖的方式安装局部依赖,只有直接依赖会被放在顶层依赖目录中 711 | - `https-proxy`:代理 712 | - `if-present`:如果置为 `true`,`npm run-script` 就不会在脚本找不到时报错 713 | - `ignore-scripts`:如果置为 `true`,npm 就不会运行 `package.json` 定义的脚本 714 | - `init-module`:指定 `npm init` 命令运行的模块 715 | - `init-author-name`:`npm init` 使用的默认作者名 716 | - `init-author-email`:`npm init` 使用的默认作者邮箱 717 | - `init-author-url`:`npm init` 使用的默认作者 URL 718 | - `init-license`:`npm init` 使用的默认许可证 719 | - `init-version`:`npm init` 使用的默认版本号 720 | - `json`:`npm ls` 等命令输出 JSON 格式的数据 721 | - `link`:如果置为 `true`,如果全局依赖中有合适的包,安装局部依赖时将会直接链接到这个全局依赖的包;如果全局依赖中没有该包的任何版本,则全局安装这个包,并链接到局部依赖中;其他情况则在局部依赖中安装该包 722 | - `long`:`npm ls` 和 `npm search` 显示额外信息 723 | - `message`:`npm version` 写在 git 提交中的信息,`%s` 将被替换为版本号 724 | - `npat`:安装时运行测试 725 | - `onload-script`:指定一个在 npm 加载时 `require()` 的包,编程使用 npm 时可能会有用 726 | - `only`:与命令中的 `--only` 效果类似 727 | - `optional`:如果置为 `false`,则不安装 `optionalDependencies` 中的依赖 728 | - `prefix`:指定安装全局依赖的路径 729 | - `production`:如果置为 `true`,则开启生产模式,`npm install` 将不安装开发依赖,声明周期脚本运行时自动设置 `NODE_ENV="production"` 730 | - `registry`:指定 npm registry 的 URL 731 | - `rollback`:移除安装失败的模块 732 | - `save`:与命令中的 `--save` 效果类似 733 | - `scope`:与命令中的 `--scope` 效果类似 734 | - `shrinkwrap`:如果置为 `false`,安装时忽略 `npm-shrinkwrap.json` 735 | - `progress`:如果置为 `false`,不显示进度条 736 | - `loglevel`:设置输出日志的 level,置为 `silly` 可以显示全部日志 737 | 738 | ### .npmignore 739 | 740 | 当模块目录中存在 `.gitignore` 但没有 `.npmignore` 时,npm 将会忽略 `.gitignore` 中的文件。如果目录中存在 `.npmignore`,npm 将会根据 `.npmignore` 忽略文件。 741 | 742 | 默认被 npm 忽略,不需要添加到 `.npmignore` 中的文件: 743 | 744 | - `.*.swp` 745 | - `._*` 746 | - `.DS_Store` 747 | - `.git` 748 | - `.hg` 749 | - `.npmrc` 750 | - `.lock-wscript` 751 | - `.svn` 752 | - `.wafpickle-*` 753 | - `config.gypi` 754 | - `CVS` 755 | - `npm-debug.log` 756 | 757 | 除了 bundled dependencies 外,node_modules 中的所有文件也会被忽略。 758 | 759 | 下面的文件即使添加到 `.npmignore` 中的文件也不会被忽略: 760 | 761 | - `package.json` 762 | - `README`(及其变体) 763 | - `CHANGELOG`(及其变体) 764 | - `LICENSE`、`LICENCE` 765 | 766 | ### scripts 767 | 768 | npm 支持的生命周期脚本有: 769 | 770 | - `prepublish`: 发布模块之前执行,也在不带任何参数的局部 `npm install` 之前执行 771 | - `publish`、`postpublish`: 发布模块之后执行 772 | - `preinstall`: 安装该模块之前执行 773 | - `install`、`postinstall`: 安装该模块之后执行 774 | - `preuninstall`、`uninstall`: 移除该模块之前执行 775 | - `postuninstall`: 移除该模块之后执行 776 | - `preversion`、`version`: 修改模块版本号之前执行 777 | - `postversion`: 修改模块版本号之后执行 778 | - `pretest`、`test`、`posttest`: 在 `test` 命令的前后执行 779 | - `prestop`、`stop`、`poststop`: 在 `stop` 命令的前后执行 780 | - `prestart`、`start`、`poststart`: 在 `start` 命令的前后执行. 781 | - `prerestart`、`restart`、`postrestart`: 在 `restart` 命令的前后执行,如果 `restart` 脚本没有提供,`restart` 命令将会执行 `stop` 脚本再执行 `start` 脚本 782 | 783 | 对于自定义名称的脚本,可以通过 `npm run-script ` 来执行,匹配名称的 *pre* 和 *post* 命令同样也会执行。 784 | 785 | ### shrinkwrap 786 | 787 | `npm shrinkwrap` 可以用来锁定依赖的版本号。 788 | 789 | 一个使用的🌰: 790 | 791 | 1. 我们有模块 A: 792 | 793 | ```js 794 | { 795 | "name": "A", 796 | "version": "0.0.1", 797 | "dependencies": { 798 | "B": "<0.1.0" 799 | } 800 | } 801 | ``` 802 | 803 | 模块 B: 804 | 805 | ```js 806 | { 807 | "name": "B", 808 | "version": "0.0.1", 809 | "dependencies": { 810 | "C": "<0.1.0" 811 | } 812 | } 813 | ``` 814 | 815 | 和模块 C: 816 | 817 | ```js 818 | { 819 | "name": "C", 820 | "version": "0.0.1" 821 | } 822 | ``` 823 | 824 | 这三个模块都只有 0.0.1 这一个版本。此时运行 `npm install A`,将会得到: 825 | 826 | ``` 827 | A@0.0.1 828 | `-- B@0.0.1 829 | `-- C@0.0.1 830 | ``` 831 | 832 | 2. 如果模块 B 发布了 0.0.2 版本,此时运行 `npm install A`,将会得到: 833 | 834 | ``` 835 | A@0.0.1 836 | `-- B@0.0.2 837 | `-- C@0.0.1 838 | ``` 839 | 840 | 但是模块 A 的作者希望安装原来的版本,那么他可以运行: 841 | 842 | ``` 843 | npm shrinkwrap 844 | ``` 845 | 846 | 然后在项目目录下得到了一个 `npm-shrinkwrap.json` 文件: 847 | 848 | ```js 849 | { 850 | "name": "A", 851 | "version": "0.0.1", 852 | "dependencies": { 853 | "B": { 854 | "version": "0.0.1", 855 | "from": "B@<0.1.0", 856 | "resolved": "https://registry.npmjs.org/B/-/B-0.0.1.tgz", 857 | "dependencies": { 858 | "C": { 859 | "version": "0.0.1", 860 | "from": "C@<0.1.0", 861 | "resolved": "https://registry.npmjs.org/C/-/C-0.0.1.tgz" 862 | } 863 | } 864 | } 865 | } 866 | } 867 | ``` 868 | 869 | `npm shrinkwrap` 命令根据当前目录中的 node_modules 目录锁定了依赖版本号,此时再运行 `npm install`,该命令的行为将变为: 870 | 871 | 1. 重新构造 `npm-shrinkwrap.json` 中描述的依赖树,如果一个依赖项中的 `resolved` 字段可用,则使用该字段获取依赖,否则使用 `version` 字段来获取依赖 872 | 2. 以普通的方式安装 `npm-shrinkwrap.json` 中缺失的依赖 873 | 874 | *** 875 | 876 | 添加或升级依赖包的方法: 877 | 878 | - 添加或升级一个依赖包 879 | 880 | ``` 881 | npm install --save 882 | ``` 883 | 884 | - 升级所有依赖包 885 | 886 | ``` 887 | npm install --no-shrinkwrap 888 | ``` 889 | 890 | 注意事项: 891 | 892 | 1. 如果 node_modules 目录中的依赖比 `package.json` 中定义的多或者少,`npm shrinkwrap` 命令将会失败 893 | 2. `npm shrinkwrap` 命令不锁定 `devDependencies` 中依赖的版本,即 `npm-shrinkwrap.json` 中不包含开发依赖;如果希望锁定开发依赖的版本,则需要在运行命令时加上 `--dev` 参数 894 | 3. shrinkwrap 不会继承,即执行过程中不会访问依赖的 `npm-shrinkwrap.json` 文件;但是,依赖中如果有 `npm-shrinkwrap.json` 文件,在安装该依赖的时就会按照这个文件来安装相关的模块到 node_modules 目录中,如果没有 `npm-shrinkwrap.json` 文件,开发者需要在使用 shrinkwrap 前自行确认 node_modules 目录中的依赖都是有效的;shrinkwrap 总是据当前目录中的 node_modules 目录中的内容锁定依赖版本号 895 | 896 | ### cache 897 | 898 | npm 将数据缓存在 `npm config get cache` 命令指定的路径中。 899 | 900 | 当局部安装一个模块时,npm 会执行以下步骤: 901 | 902 | 1. 检查缓存中的模块信息文件(.cache.json,包括 ETag 等信息)是否存在并在免检时间内 903 | 2. 如果存在合适的缓存信息文件且没有超过免检时间,执行步骤 6 904 | 3. 获取该模块的最新信息文件,然后计算出合适的模块版本号 905 | 4. 如果缓存中有该版本号的模块,则执行步骤 6 906 | 5. 下载该版本号的模块,并将其载入缓存中 907 | 6. 取缓存中的文件,并将其安装至目标路径中 908 | 909 | `npm cache` 命令的用法: 910 | 911 | 1. 向缓存中添加指定的模块: 912 | 913 | ``` 914 | npm cache add 915 | npm cache add 916 | npm cache add 917 | npm cache add @ 918 | ``` 919 | 920 | 2. 显示缓存中的数据: 921 | 922 | ``` 923 | npm cache ls [] 924 | ``` 925 | 926 | 3. 清空缓存: 927 | 928 | ``` 929 | npm cache clean [] 930 | ``` 931 | 932 | npm 缓存的改进方案: 933 | 934 | 1. [local-npm](https://github.com/nolanlawson/local-npm):一个本地 npm 镜像,但是仅缓存已经安装过的模块,没有网络时自动回退到本地 935 | 936 | 2. [npm_lazy](https://github.com/mixu/npm_lazy) 937 | --------------------------------------------------------------------------------