文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

Node.js 使得用 Javascript 编写服务器端应用程序成了可能。它建立在 V8 JavaScript 运行时 ,用 C++ 写成— 因此它运行起来很快。 它原本是打算用作应用程序的一个服务器环境, 但开发者开始使用它来创建工具在本地任务自动化方面来辅助他们。从那时起,一个全新的基于 Node 的生态环境工具 (例如 Grunt, GulpWebpack) 开始演化改变前端开发的面貌。

这篇受欢迎的文章于 2017.06.08 更新,来反映 npm 的现状,同样也反映了第5版发布带来的变化。

第 1 段(可获 1.33 积分)

为了使用 Node.js 里的这些工具(或者包),我们需要安装并以一种有效的方式管理它们。这就是 npm,Node 包管理器的所在。它安装你想使用的包并提供有用的界面来操作它们。

在这篇文章里,我准备着眼于使用 npm 的基础知识。 我将展示如何在局部模式和全局模式安装包,以及删除、更新和安装特定版本的包。我也将展示如何使用 package.json 来管理项目的依赖性。如果你是个更喜欢观看视频的人,为何不注册 SitePoint Premium 并观看我们的免费录播: 什么是 npm 我如何使用它?

第 2 段(可获 1.71 积分)

我们开始使用 npm 之前,首先得在系统上安装 Node.js。现在我们这样做…

安装 Node.js

更多关于作者

前往 Node.js 下载页 并获取你需要的版本。有 Windows 和 Mac 安装程序可用,以及预编译 Linux 二进制和源码版本。对 Linux,你也可以通过包管理器来安装Node, 依照这里的概述.

这个教程我们准备用 v6.10.3 稳定版。我写这文章的时候,这是现行的 Node 长期支持版 (LTS).

第 3 段(可获 1.43 积分)

提示: 你或许也想 使用版本管理器安装 Node. 这使得下节的的权限问题无效。

让我们看看node安装到哪里并检查下版本。

$ which node
/usr/bin/node
$ node --version
v6.10.3

为确认你的安装是否成功,我们让 Node 的 REPL(交互式解释器) 尝试下。

$ node
> console.log('Node is running');
Node is running
> .help
.break Sometimes you get stuck, this gets you out
.clear Alias for .break
.exit  Exit the repl
.help  Show repl options
.load  Load JS from a file into the REPL session
.save  Save all evaluated commands in this REPL session to a file
> .exit
第 4 段(可获 0.59 积分)

Node.js 安装好了,现在我们可以把注意力集中在 npm,它已经包含在安装中。

$ which npm
/usr/bin/npm
$ npm --version
3.10.10

Node 打包模块

npm 能在局部或全局模式安装包。在局部模式,它在父工作目录的 node_modules 文件夹中安装包。这个位置归当前用户所有。全局包安装在root用户 的 {prefix}/lib/node_modules/ 目录 ({prefix} 一般是 /usr/ or /usr/local)。这意味着你得使用 sudo 来全局地安装包, 这有可能当解析第三方依赖以及安全问题时导致权限错误。我们来改下:

第 5 段(可获 1.24 积分)

是管理这些包的时候了

改变全局包的位置

看下  npm config 的输出是什么。

$ npm config list
; cli configs
user-agent = "npm/3.10.10 node/v6.10.3 linux x64"

; userconfig /home/sitepoint/.npmrc
prefix = "/home/sitepoint/.node_modules_global"

; node bin location = /usr/bin/nodejs
; cwd = /home/sitepoint
; HOME = /home/sitepoint
; "npm config ls -l" to show all defaults.

这提供给我们关于安装的信息。目前获取到当前的全局位置非常重要。

$ npm config get prefix
/usr

这个是我们想改变的前缀,以便安装全局包到我们的 home 目录。为达成此目的,在你的 home 文件夹来创建新目录。

第 6 段(可获 0.81 积分)
$ cd ~ && mkdir .node_modules_global
$ npm config set prefix=$HOME/.node_modules_global

通过简单的配置更改,我们修改了全局 Node 包安装的位置,同时也在 home 目录创建了一个 .npmrc 文件。

$ npm config get prefix
/home/sitepoint/.node_modules_global
$ cat .npmrc
prefix=/home/sitepoint/.node_modules_global

 root 的位置仍然有安装好的 npm。但是因为我们改变了全局包的位置,我们能利用这点。我们需要重新安装 npm,但这次安装在新的用户下,也安装最新版本的 npm。

第 7 段(可获 0.93 积分)
$ npm install npm --global
└─┬ npm@5.0.2
  ├── abbrev@1.1.0
  ├── ansi-regex@2.1.1
....
├── wrappy@1.0.2
└── write-file-atomic@2.1.0

最后,我们需要把 .node_modules_global/bin 增加到环境变量 $PATH,以便能从命令行运行全局包。添加如下一行到 .profile,.bash_profile 或者.bashrc ,然后重启你的终端。

export PATH="$HOME/.node_modules_global/bin:$PATH"

现在一开始就可以找到 .node_modules_global/bin 并且使用的是正确版本的 npm 了。

$ which npm
/home/sitepoint/.node_modules_global/bin/npm
$ npm --version
5.0.2
第 8 段(可获 0.61 积分)

用全局模式安装包

此时我们只全局地安装了一个包 —  npm 包自身。我们来改变这种状况,安装下 UglifyJS (一种 JavaScript 压缩工具)。我们使用  --global 标记,但是可以缩写成-g

$ npm install uglify-js --global
/home/sitepoint/.node_modules_global/bin/uglifyjs -> /home/sitepoint/.node_modules_global/lib/node_modules/uglify-js/bin/uglifyjs
+ uglify-js@3.0.15
added 4 packages in 5.836s

你可以从输出中看到,额外的包 —  UglifyJS 的依赖被安装了。

第 9 段(可获 0.74 积分)

列出全局包

我们可以用 npm list 命令列出安装过的全局包。

$ npm list --global
home/sitepoint/.node_modules_global/lib
├─┬ npm@5.0.2
│ ├── abbrev@1.1.0
│ ├── ansi-regex@2.1.1
│ ├── ansicolors@0.3.2
│ ├── ansistyles@0.1.3
....................
└─┬ uglify-js@3.0.15
  ├─┬ commander@2.9.0
  │ └── graceful-readlink@1.0.1
  └── source-map@0.5.6

然而,输出太详尽了。我们用 --depth=0 选项修改下。

$ npm list -g --depth=0
/home/sitepoint/.node_modules_global/lib
├── npm@5.0.2
└── uglify-js@3.0.15
第 10 段(可获 0.35 积分)

这样好多了 — 正是我们安装的包和它们的版本号。

任何全局安装的包在命令行下都是可用的。例如, 如下是如何使用 Uglify 包来精简 example.jsexample.min.js

$ uglifyjs example.js -o example.min.js

以局部模式安装包

局部安装包时,一般使用 package.json 文件来安装。我们来开始创建一个吧。

$ npm init
package name: (project)
version: (1.0.0)
description: Demo of package.json
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
第 11 段(可获 0.79 积分)

按下 Enter 键接收默认值,然后键入 yes 确认。这样就在项目的根目录创建了一个 package.json 文件。

{
  "name": "project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

提示: 如果你想快速生成一个 package.json 文件,请使用 npm init --y

这些字段相当的不言自明的,除了 main 和 scripts。 main 字段是你的程序首要入口点而 scripts 字段让你指定在包的生命周期内运行可变次数的脚本命令。我们暂时不管这些,但如果你想了解更多的东西,请参考 package.json documentation on npm 和这篇文章 using npm as a build tool.

第 12 段(可获 1.36 积分)

现在我们尝试安装 Underscore.

$ npm install underscore
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN project@1.0.0 No description
npm WARN project@1.0.0 No repository field.

+ underscore@1.8.3
added 1 package in 0.344s

注意到一个锁文件创建了。我们一会儿再回到这儿。

现在如果我们看下 package.json 可以看到添加了一个 dependencies 字段:

{
  ...
  "dependencies": {
    "underscore": "^1.8.3"
  }
}

使用 package.json 管理依赖

如你所见,Underscore v1.8.3 已安装到我们的项目中。 脱字符 (^) 在版本号的前面表明当安装的时候, npm 将拉入它能找到的只需主版本匹配的包的最高版本 (除非 package-lock.json 文件存在)。在这个例子中, 可以是任何低于 v2.0.0 的版本。这种版本依赖方法 (major.minor.patch) 被称为语义版本控制。你可以从这里阅读更多关于这块的内容: Semantic Versioning: Why You Should Be Using it.

第 13 段(可获 1.68 积分)

同时注意到 Underscore 被保存为 dependencies 字段的一个属性。这已经是最新的 npm 版本的默认行为并且用作应用程序运行所需要的包 (比如 Underscore)。 有可能通过指定 --save-dev 标记保存为一个 devDependency 包。 devDependencies  是用作开发目的的包,比如,用作运行测试或者 转移成堆(transpiling)的代码。

你也可以把 private: true 添加到 package.json 来阻止个人仓库的意外发布以及抑制 npm 运行安装时产生的任何警告。

第 14 段(可获 1.08 积分)

迄今为止使用 package.json 来指定项目依赖项的最大理由就是可移植性。例如,当你克隆他人的代码,你只需在项目根目录运行 npm i,npm 将解析并获取所有必需的包来运行程序。我们稍后详细查看这点。

完成这节之前,我们来快速的检查下 Underscore 的运行 。在项目根目录创建 test.js 文件并添加如下内容:

const _ = require('underscore');
console.log(_.range(5));

node test.js 运行文件,你可以看到屏幕输出 [0, 1, 2, 3, 4]

第 15 段(可获 1.23 积分)

卸载局部包

npm 是一个包管理器,因此它必须能够移除包。假定当前 Underscore 包导致了兼容性问题。我们可以像下面那样移除此包,安装更旧的版本:

$ npm uninstall underscore
removed 2 packages in 0.107s
$ npm list
project@1.0.0 /home/sitepoint/project
└── (empty)

安装特定版本的包

我们现在安装我们期望版本的 Underscore 包。我们可以通过使用 @ 标识来追加版本号。

$ npm install underscore@1.8.2
+ underscore@1.8.2
added 1 package in 1.574s

$ npm list
project@1.0.0 /home/sitepoint/project
└── underscore@1.8.2
第 16 段(可获 0.91 积分)

更新包

我们检查下是否有 Underscore 包的更新:

$ npm outdated
Package     Current  Wanted  Latest  Location
underscore    1.8.2   1.8.3   1.8.3  project

Current 栏位表明局部安装包的版本。Latest 栏位告诉我们这个包的最新版本。Wanted 栏位则是不破坏现有代码的情况下能升级到的最新版本。

还记得前面的 package-lock.json 文件吗? 它在 npm v5 被引进,这个文件的目的是确保依赖在所有项目安装的机器上保持一致。它在 npm 对node_modules 文件夹或者 package.json 文件的任何改动时自动产生 。

第 17 段(可获 1.3 积分)

如果你愿意你可以进一步尝试。 删除 node_modules 文件夹,然后重新运行 npm i。最新版本的 npm 将按照 Underscore v1.8.2 (这也是 package-lock.json 文件指定的)。早期的 npm 版本由于语义版本原因将拉取 v1.8.3。 过去不一致的包版本已被证实是开发者非常头痛的问题。 一般地,通过使用 npm-shrinkwrap.json 文件来解决这个问题,这个文件需要手动创建。

 现在假定 Underscore 的最新的版本修复了一些以前的错误,我们想升级包到这个版本。

第 18 段(可获 1.29 积分)
$ npm update underscore
+ underscore@1.8.3
updated 1 package in 0.236s

$ npm list
project@1.0.0 /home/sitepoint/project
└── underscore@1.8.3

提示:为使它能工作,Underscore 需要在 package.json 作为一个依赖列出来。如果有很多过时的模块想升级,我们也可以执行 npm update。

查找包

我们已经在教程中多次使用 mkdir 命令。那这存在相同的 node 包吗? 我们来使用下 npm search 命令。

$ npm search mkdir
NAME      | DESCRIPTION          | AUTHOR          | DATE       | VERSION
mkdir     | Directory crea…      | =joehewitt      | 2012-04-17 | 0.0.2
fs-extra  | fs-extra conta…      | =jprichardson…  | 2017-05-04 | 3.0.1
mkdirp    | Recursively mkdir,…  | =substack       | 2015-05-14 | 0.5.1
...
第 19 段(可获 0.69 积分)

 它是(mkdirp)。我们来安装它。

$ npm install mkdirp
+ mkdirp@0.5.1
added 2 packages in 3.357s

现在创建了 mkdir.js 文件,复制-粘贴这段代码:

const mkdirp = require('mkdirp');
mkdirp('foo', function (err) {
  if (err) console.error(err)
  else console.log('Directory created!')
});

并在终端运行:

$ node mkdir.js
Directory created!

重装项目依赖

我们先再安装一个包:

$ npm install request
+ request@2.81.0
added 54 packages in 15.92s

检查下 package.json.

"dependencies": {
  "mkdirp": "^0.5.1",
  "request": "^2.81.0",
  "underscore": "^1.8.2"
},
第 20 段(可获 0.41 积分)

注意依赖项清单自动更新了。 在以前的 npm 版本,你也许得执行 npm install request --save 来在 package.json 中保存依赖 如果你想安装包并不想保存它到 package.json, 只需使用 --no-save 参数。

假定你已经克隆项目源码到另一台机器,并需要安装依赖。我们先删除 node_modules 文件夹,然后执行 npm install

$ rm -R node-modules
$ npm list
project@1.0.0 /home/sitepoint/project
├── UNMET DEPENDENCY mkdirp@^0.5.1
├── UNMET DEPENDENCY request@^2.81.0
└── UNMET DEPENDENCY underscore@^1.8.2

npm ERR! missing: mkdirp@^0.5.1, required by project@1.0.0
npm ERR! missing: request@^2.81.0, required by project@1.0.0
npm ERR! missing: underscore@^1.8.2, required by project@1.0.0

$ npm install
added 57 packages in 1.595s
第 21 段(可获 0.83 积分)

看下 node_modules 文件夹,你会发现它又被创建了。通过这种方式,你可以轻松与他人共享代码而不会使你的项目和代码仓库充满依赖而显得臃肿。

管理缓存

当 npm 安装包时会保留一个副本,因此下次你想安装包时就不需要连接互联网。这些副本缓存在你的 home 路径下 .npm 目录。

$ ls ~/.npm
anonymous-cli-metrics.json  _cacache  _locks  npm  registry.npmjs.org

天长日久旧包增多,这个目录将变得混乱,因此经常清理这个目录是很有帮助的。

第 22 段(可获 1.16 积分)
$ npm cache clean

如果系统上有多个想清理的 node 项目,你也可以清理所有工作区的 node_module 文件夹。

find . -name "node_modules" -type d -exec rm -rf '{}' +

别名

你或许已注意到,有很多方法来运行 npm 命令。以下是一些常用简短的 npm 别名清单:

  • npm i <package> – 安装局部包
  • npm i -g <package> – 安装全局包
  • npm un <package> – 卸载局部包
  • npm up – npm 更新包
  • npm t – 运行测试
  • npm ls – 列出安装过的模块
  • npm ll or npm la – 列出模块的同时打印额外的包信息
第 23 段(可获 0.94 积分)

你也可以像下面那样一次安装多版本的包:

$ npm i express momemt lodash mongoose body-parser webpack

如果你想学习所有常用的 npm 命令,执行 npm help 来获取完整的清单。你也可以在下面这篇文章上学习更多 10 Tips and Tricks That Will Make You an npm Ninja.

版本管理

有很多可用的工具让你在同一台机器管理多版本的 Node.js。有一种工具是 n。另一种工具是 nvm (Node 版本管理器)。如果你对它们感兴趣,为何不到我们的教程上看看呢: Install Multiple Versions of Node.js using nvm.

第 24 段(可获 1.28 积分)

结语

在这篇教程,我已经覆盖了使用 npm 的 基础部分。我演示了如何从项目下载页安装 Node.js, 如何改变全局包的位置 (因此可以避免使用 sudo),如何在本地和全局模式安装包。我也包括了删除,更新,安装特定版本的包,以及管理项目依赖。如果你想学习更多最新版本的新特性,你可以参考 npm Github releases page.

在第5版,在前端开发上 npm 正大步走向世界。 根据它的首席运行官(COO), 它的用户根基正在改变,大部分用户完全不用它来写 Node。更确切的说,它正变成一个用来装配 JavaScript 的前端工具(认真来说,你可以用它来安装任何东西),一个书写现代 JavaScript 的主要部分。现在你在项目中使用 npm 吗?如果没有,现在开始应该是个好时机。

第 25 段(可获 2.34 积分)

文章评论

访客
感觉直译很严重,为何不在翻译完后检查一遍呢?很多语句没有按中文习惯作调整