曾小涛

npm 入门教程

原文链接: www.sitepoint.com

Node.js 的出现使得用 Javascript 写服务端应用成为可能。Node.js 由 C++ 编写而成并且构建于 V8 引擎之上,因此运行得很快。一开始,Node.js 只是想运行于服务端环境,但是开发者们显然不满足于此,开始创造各种工具来实现自动化任务。也因为此,基于 Node 的前端自动化工具(如Grunt, GulpWebpack)的出现也给前端开发带来了翻天覆地的变化。

本文最后更新时间为 08.06.2017, 介绍了包括了 npm 当前现状,以及 npm5 的一些改变。

想要利用这些工具或者模块包,我们需要使用合理的方式来安装和管理这些模块,这也是 npm 这个包管理工具出现的初衷。我们通过 npm 来安装模块并且使用其提供的接口来使用这些模块。

在本文中,我将要介绍 npm 的基本用法,告诉你们怎么安装安装本地和全局模块包,怎么删除,升级和安装一个特定版本的模块。同时我还会介绍怎么利用 package.json 文件来管理你的依赖。如果你喜欢观看视频,也可以注册成为 SitePoint Premium 的会员,观看我们的免费视频: What is npm and How Can I Use It?

在我们开始使用 npm 之前,我们必须先在本地系统中安装 Node.js。让我们开始吧。

安装 Node.js

作者的更多文章

首先前往 Node.js 的下载页面获取你需要的版本。下载页面提供了 Windows 和 Mac 的安装包以及预编译的 Linux 二进制源代码,对于 Linux 用户,你还可以通过包管理工具来安装。

写这篇教程时,Node.js 的 LTS 版本为 v6.10.3,因此本文也会以此版本来讲解。

提示:你可能想通过版本管理工具来安装 Node.js,我们将会在后面的章节介绍。

安装之后让我们来看看 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

至此 Node.js 已经安装完成,让我们来看看 npm, 它已经随着 Node.js 一起安装在了你的系统中。

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

Node模块

npm 可以以全局或者本地模式安装模块。本地模式下,所有的模块包安装在你当前工作目录下的 node_modules 文件夹中,能被当前用户访问到。全局模式则是将模块安装在 {prefix}/lib/node_modules/ 中,为 root 用户所有({prefix} 通常是 /usr/ 或者 /usr/local])。这意味着当你想全局安装模块的时候,出于安全性的考虑和避免解析第三方的依赖模块时可以引发的权限错误,你需要使用 sudo 命令。

Parcel delivery company

Time to manage those packages

更改全局模块的安装位置

输入 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 命令提供了我们的安装的信息。现在,让我们获取它们的安装位置。

$ npm config get prefix
/usr

This is the prefix we want to change, so as to install global packages in our home directory. To do that create a new directory in your home folder.

$ cd ~ && mkdir .node_modules_global
$ npm config set prefix=$HOME/.node_modules_global

为了在我们的 home 目录安装全局模块,我们要更改的 prefix 就是这个,我们首先在 home 目录中创建一个新文件夹。

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

我们仍然在 root 用户目录下安装了 npm, 但是由于我们该改变了全局模块的安装位置,我们需要再一次安装 npm,这一次我们会安装最新版本的 npm5到新用户目录。

$ 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

安装全局模块

到目前为止我们只安装了 npm 这一个全局模块。现在再安装 UglifyJS (一个 JavaScript 压缩工具)。使用 --flag 标记,当然也可以简写成 -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

你可以看到,这个过程也安装了其他的模块,它们都是 Uglify 的所依赖的模块。

列出所有全局模块

通过 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

输出信息现在更清楚了。我们只列出了我们安装的模块和版本号。

所有全局安装的模块都可以通过命令行访问到。举个例子,现在你想要使用 Uglify 模块来压缩 example.js 文件,输出文件为 example.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)

按下 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"
}

小技巧:使用 npm init --y 能更快的创建 package.json文件。

package.json 的除了 mainscripts,每一项的意思的都很明确,具有 “自解释性”。main 这一项表示你的程序的入口文件,scripts 表示你项目生命周期里定义的可运行的脚本命令。到此,package.json 文件的这些项已经足够,如果你想了解更多关于每一项的含义,你可以看看 npm 的文档 和这篇文章 using npm as a build tool.

现在,我们来安装 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

注意到,安装过程中创建了一个 lockfile 文件,我们之后会介绍它。

现在,再看一看 package.json 文件,我们看到它多了 dependencies 这一项。

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

使用 package.json 文件来管理依赖包

正如你所见,你的工程项目已经安装了 1.8.3 版本的 Undescore。版本号前面的 ^ 符号表示当你安装此模块的时候,npm 会拉取它能发现的关于此模块的最新版本,但是必须匹配主版本号(如果本地没有 package-lock.json 文件)。在本例中,npm 会寻找低于 2.0.0 版本的最新版的 Underscore。这种模块版本安装方法叫做 语义化版本。你可以阅读这篇文章 Semantic Versioning: Why You Should Be Using it 来了解更多。

同样要注意到,Underscore 被保存到了 dependencies 这一项了。在最新版本的 npm 也就是 npm5 中,这已经成为了默认选项,表示程序运行时的必需模块。你也可以指定 --save-dev 选项来把模块信息添加到 devDependencies 这一项,表示开发时所需的模块,比如说测试或者代码转换。

你也在 package.json 文件中添加 private: true 这一项,防止你的私人仓库被误发布,同时运行 npm install 命令时,避免产生任何警告信息。

到目前为止,最应该使用 package.json 的理由是 package.json 指定了所有的依赖项,所以当我们想克隆别人的代码,我们只需要复制 package.json 文件到项目的根目录并运行 npm i,npm 将会自动分析文件并且安装所有我们所需的模块。我们在后面还会详细讲解。

在结束这一部分之前,我们来检查一下 underscore 是否正常工作。我们在根目录创建一个 test.js 文件,并且将如下代码添加到代码中。

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

在控制台输入 node test.js 来运行这个文件,屏幕上会输出 [0, 1, 2, 3, 4]

卸载本地模块

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

更新一个模块

让我们检查一下 underscore 是否有更新

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

Current 这一列告诉我们我们本地安装的 underscore 版本,Latest 这一列则告诉我们当前的 underscore 的最新版本,Wanted 这一列则表示在不破坏当前代码的前提下,我们可以升级的最新版本。

记得我们之前所说的 package-lock.json 文件吗? npm5 开始引入这个文件,目的是记住安装的依赖包的版本号,从而使每台机器上安装的依赖包的版本都相同。每当你修改了 node_modules 文件夹或者 package.json 文件,package-lock.json 文件将会自动生成。

你可以尝试一个这个特性。删除 node_modules 文件夹,重新运行 npm i 。新版本的 npm5 将会安装 v1.8.2 版本的 Underscore。(因为在 package-lock.json 指定了 Underscore 的版本为 1.8.2)。而旧版本的 npm 会安装 1.8.3 版本的 Underscore。之前的经验证明,版本不一致的依赖模块将会给开发者带来很大的麻烦,在之前,你需要手动创建一个 npm-shrinkwrap.json 文件来避免这个问题。

现在,假设 Underscore 的最新版本已经修复了我们之前遇到的 bug, 我们现在要更新 Underscore 至最新版本。

$ 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 的 dependency 中。另外,如果你也很多过时的模块,可以执行 npm update 命令一起更新所有模块。

搜索一个模块

你可以看到,这篇教程你我们已经多次使用了 mkdir 命令,那么是否有一个模块可以实现和 mkdir 一样的功能,我们有 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
...

我们找到了(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"
},

注意到,dependencies 列表也自动更新了。在之前的 npm 版本,只有执行 npm install request --save 命令才会将 dependency 保存到 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

检查一下你的 node_modules 文件夹,你会发现所有的模块都已经重新安装了。通过这种方式,你可以很简单的与其他人分享你的工程文件,你只需给他们一个 package.json 文件,而不用把你的所有依赖模块给他们。

管理缓存

当你通过 npm 安装一个模块时,它会保存一份模块的副本,所以当你下次安装同样的模块的时候,npm 它不会访问网络,而是直接从你当前工作目录的 .npm 文件夹下读取缓存。

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

但是随着时间的推移,你的 .npm 文件夹会有很多夹杂许多旧的模块包,这时就很有必要定期清理一下你的 .npm 目录了。

`$ npm cache clean`

如果你的想清除你系统内有多个 node 工程,你也在你的工作目录下使用以下命令删除所有的 node_modules 文件夹。

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

别名

你可能注意到了,这里有很多方式去执行我们的 npm 命令。这里有一个简单的 npm 别名的列表

  • npm i – install local package 本地安装模块

  • npm i -g – install global package 全局安装模块

  • npm un – uninstall local package 卸载本地模块

  • npm up – npm update packages 更新本地模块

  • npm t – run tests 运行测试

  • npm ls – list installed modules 列出已安装的模块

  • npm ll or npm la – print additional package information while listing modules 打印出所列模块的附加信息

你也可以像这样一次安装多个模块

`$ 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 版本工具可供使用来管理你系统内的 Node.js 版本。比如说 n,或者nvm (Node Version Manager). 如果你对他们感兴趣,你应该读一下我这篇入门指南: Install Multiple Versions of Node.js using nvm.

结论

在这篇教程中,我介绍了 npm 的基本用法,包括如何安装 Node.js、如何改变全局模块的安装位置(这样我们可以避免使用 sudo)、如果本地或者去全局安装 npm 模块、同时还介绍了怎么删除,升级模块,怎么安装一个指定版本的模块以及怎么管理你工程的各个依赖项。如果你想了解关于更多的 npm 最新版本的特性,你可以访问 npm 的 官方仓库

npm5 给全世界前端开发带来了巨大的改进。其 COO 说,npm 的用户群在不断变化,大部分用户不仅仅为了写 Node 程序而使用 npm, npm 已经成为了前端整合 JavaScript 代码的工具,甚至是现代 JavaScript 不可或缺的一部分。如果你还没有在你的项目中使用 npm ,现在是时候了。

这篇文章由Graham Cox审核。感谢 SitePoint 上的审核人员,是你们让 SitePoint 的内容变得更好。