npm使用说明

介绍 npm 的使用。

npm简介

npm有两层含义。一层含义是Node的开放式模块登记和管理系统,网址为npmjs.org。另一层含义是Node默认的模块管理器,是一个命令行下的软件,用来安装和管理Node模块。

npm不需要单独安装。在安装Node的时候,会连带一起安装npm。但是,Node附带的npm可能不是最新版本,使用下方命令查看npm的版本。

1
2
3
$ npm -v
# or
$ npm --version

如果不是最新的,最好用下面的命令,更新到最新版本:

1
$ npm install npm@latest -g

基本命令

npm list

npm list 列出项目的依赖信息。也可以使用 npm ls 简写命令。

1
2
3
4
5
6
7
8
9
10
11
# 列出当前项目的说有依赖项
$ npm list

# 只列出一层深度依赖项
$ npm list --depth 0

# 列出在全局的所有安装包信息
$ npm list -g

# 只列出全局的一层递归深度的安装包信息
$ npm list -g --depth 0

npm init

使用npm init初始化包。即初始化 package.json 文件。

1
2
3
4
5
6
7
# 会有一系列提问设置,根据提问设置package.json文件中的基本字段内容
$ npm init

# 跳过提问环节,直接使用默认设置初始化package.json文件
$ npm init -y
# or
$ npm init -yes

我们可以为 init 命令初始化一些值,如:

1
2
3
> npm set init.author.email "yisiwings@163.com"
> npm set init.author.name "hongwenqing"
> npm set init.license "MIT"

npm install

Node模块采用npm install命令安装。

每个模块可以“全局安装”,也可以“本地安装”。“全局安装”指的是将一个模块安装到系统目录中,各个项目都可以调用。一般来说,全局安装只适用于工具模块,比如eslint@vue/cli。“本地安装”指的是将一个模块下载到当前项目的node_modules子目录,然后只有在项目目录之中,才能调用这个模块。

1
2
3
4
5
6
# 本地安装
$ npm install <package name>

# 全局安装
$ sudo npm install --global <package name>
$ sudo npm install -g <package name>

安装之前,npm install会先检查,node_modules目录之中是否已经存在指定模块。如果存在,就不再重新安装了,即使远程仓库已经有了一个新版本,也是如此。

如果你希望,一个模块不管是否安装过,npm 都要强制重新安装,可以使用-f--force参数。

1
$ npm install <package name> --force

如果你希望,所有模块都要强制重新安装,那就删除node_modules目录,重新执行npm install

1
2
$ rm -rf node_modules
$ npm install

install命令总是安装模块的最新版本,如果要安装模块的特定版本,可以在模块名后面加上@版本号

1
2
3
$ npm install sax@latest
$ npm install sax@0.1.1
$ npm install sax@">=0.1.0 <0.2.0"

如果使用--save-exact参数,会在package.json文件指定安装模块的确切版本。

1
$ npm install readable-stream --save --save-exact

install命令可以使用不同参数,指定所安装的模块属于哪一种性质的依赖关系,即出现在packages.json文件的哪一项中。

–save:模块名将被添加到dependencies,可以简化为参数-S。
–save-dev: 模块名将被添加到devDependencies,可以简化为参数-D。

1
2
3
4
5
$ npm install sax --save
$ npm install node-tap --save-dev
# 或者
$ npm install sax -S
$ npm install node-tap -D

npm install默认会安装dependencies字段和devDependencies字段中的所有模块,如果使用--production参数,可以只安装dependencies字段的模块。

1
2
3
$ npm install --production
# 或者
$ NODE_ENV=production npm install

如果你的网速很慢,建议使用淘宝镜像代理。安装完后,你就可以使用 cnpm 代替 npm 命令。npm 特有的一些命令除外,比如:npm addusernpm loginnpm publish等等。

1
2
# 安装cnpm 命令行工具 ,使用淘宝的npm 镜像
$ npm install -g cnpm --registry=https://registry.npm.taobao.org

npm update

npm update命令可以更新本地安装的模块。

1
2
3
4
5
# 升级当前项目的指定模块或所有可以更新的模块
$ npm update [package name]

# 升级全局安装的模块
$ npm update --global [package name]

它会先到远程仓库查询最新版本,然后查询本地版本。如果本地版本不存在,或者远程版本较新,就会安装。在升级包时,npm 会根据 package.json 文件中定义的 语义化版本规则 判断应该更新安装哪个版本的包。使用

npm outdated 命令可以准确查看通过 语义化版本规则 计算出来的版本号(Wanted字段)。

执行更新命令后 package.json 里面模块的版本号变化:

1
2
3
4
5
6
7
8
9
// 更新之前的package.json
dependencies: {
dep1: "^1.1.1"
}

// 更新之后的package.json
dependencies: {
dep1: "^1.2.2"
}

:warning: 注意,从npm v2.6.1 开始,npm update只更新顶层模块,而不更新依赖的依赖,以前版本是递归更新的。如果想取到老版本的效果,要使用下面的命令:

1
$ npm --depth 9999 update

npm uninstall

npm uninstall命令,卸载已安装的模块。

1
2
3
4
5
# 卸载指定的本地模块
$ npm uninstall [package name]

# 卸载全局模块
$ npm uninstall [package name] --global

npm run

npm不仅可以用于模块管理,还可以用于执行脚本。package.json文件有一个scripts字段,可以用于指定脚本命令,供npm直接调用。

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "myproject",
"devDependencies": {
"jshint": "latest",
"browserify": "latest",
"mocha": "latest"
},
"scripts": {
"lint": "jshint **.js",
"test": "mocha test/"
}
}

其他命令

npm link 命令可以将某个模块关联安装到 nodejs 全局环境中,以便可以在本机的任何项目中引用她。

比如,我们在构建自己的模块的时候,我们需要对其进行测试,在没有发布到 npmjs 上之前,我们无法使用如下命令来安装她:

1
npm install my-module

这时,npm link 就可以派上用场了,我们 cd 进入到 my-module 项目所在根目录后,打开终端命令行,键入:

1
npm link

此时,my-module 模块的副本已经被关联并安装到 nodejs 的全局环境中。

1
$ E:\nodejs\node_modules\my-module -> C:\Users\yisiw\Desktop\my-module

当要使用 my-module 模块时,可以直接在项目内命令行中输入:

1
npm link my-module

my-module 模块就会被安装在 node_mnodules 文件夹中,此时,你就可以使用导入语法导入她了

1
const foo = require('my-module')

这在编写自己的模块,需要测试的时候尤其有用。

npm home,npm repo

npm home 命令可以打开一个模块的主页,npm repo 命令则是打开一个模块的代码仓库。

1
2
$ npm home <package name>
$ npm repo <package name>

这两个命令不需要模块先安装。

npm outdated

npm outdated 命令检查当前项目所依赖的模块,是否已经有新版本。

1
$ npm outdated

它会输出当前版本(current version)、应当安装的版本(wanted version)和最新发布的版本(latest version)。

注:(wanted version) 是根据 package.json 文件中的依赖字段申明的语义化版本规则推算出来的。详见下方的语义化版本规则 查看说明。

npm prune

npm prune 检查当前项目的node_modules目录中,是否有package.json里面没有提到的模块,然后将所有这些模块输出在命令行。

1
$ npm prune

npm shrinkwrap

npm shrinkwrap 的作用是锁定当前项目的依赖模块的版本。

1
$ npm shrinkwrap

运行该命令后,会在当前项目的根目录下生成一个npm-shrinkwrap.json文件,内容是node_modules目录下所有已经安装的模块,以及它们的精确版本。

下次运行npm install命令时,npm发现当前目录下有npm-shrinkwrap.json文件,就会只安装里面提到的模块,且版本也会保持一致。

npm owner

模块的维护者可以发布新版本。npm owner 命令用于管理模块的维护者。

1
2
3
4
5
6
7
8
# 列出指定模块的维护者
$ npm owner ls <package name>

# 新增维护者
$ npm owner add <user> <package name>

# 删除维护者
$ npm owner rm <user> <package name>

npm deprecate

如果想废弃某个版本的模块,可以使用npm deprecate命令。

1
$ npm deprecate my-thing@"< 0.2.3" "critical bug fixed in v0.2.3"

运行上面的命令以后,小于0.2.3版本的模块的package.json都会写入一行警告,用户安装这些版本时,这行警告就会在命令行显示。

npm adduser

npm adduser用于在npmjs.com注册一个用户。

1
2
3
4
$ npm adduser
Username: YOUR_USER_NAME
Password: YOUR_PASSWORD
Email: YOUR_EMAIL@domain.com

npm login

用于登陆npm

1
$ npm login

npm publish

登陆过后可以使用 npm publish 发布一个版本。

1
$ npm publish

如果当前模块是一个beta版,比如1.3.1-beta.3,那么发布的时候需要使用tag参数,将其发布到指定标签,默认的发布标签是latest。

1
$ npm publish --tag beta

package.json文件

每个项目的根目录下面,一般都有一个package.json文件,定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。npm install命令根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。

package.json 文件至少要有两部分内容:

  • name

全部小写,没有空格,可以使用下划线或者横线。

  • version

x.x.x 的格式,符合语义化版本规则

例:

1
2
3
4
{
"name": "my-module",
"version": "1.0.0"
}

其他内容:

  • description:描述信息,有助于搜索

  • main: 入口文件,一般都是 index.js

  • scripts:支持的脚本,默认是一个空的 test

  • keywords:关键字,有助于在人们使用 npm search 搜索时发现你的项目

  • author:作者信息

  • license:默认是 MIT

  • bugs:当前项目的一些错误信息,如果有的话

  • dependencies:项目生产过程中的依赖包

  • devDependencies:项目开发、测试过程中的依赖包

  • homepage: 该包的项目主页地址

例:

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
{
"name": "demo",
"version": "1.0.0",
"description": "a vue project",
"main": "index.js",
"scripts": {
"build": "node build/build.js",
"dev": "webpack-dev-server build/webpack.dev.conf.js",
"start": "npm run dev"
},
"keywords": [
"vue"
],
"author": "hongwenqing",
"license": "MIT",
"dependencies": {
"vue": "^2.5.17",
"vuex": "~3.0.1"
},
"devDependencies": {
"babel-core": "^6.14.0",
"babel-loader": "^6.2.5",
"babel-preset-es2015": "^6.18.0",
"eslint": "^3.5.0",
"vue-loader": "^10.0.2",
"serve": "^1.4.0",
"webpack": "^4.13.2",
"webpack-dev-server":"^3.1.10"
},
"homepage": "https://www.github.com/yisibell/hello-npm-test.git"
}

语义化版本规则

官方说明地址

dependenciesdevDependencies 的内容,以 "vue": "^2.5.17" 为例,我们知道 key 是依赖的包名称,value 是这个包的版本。那版本前面的^~或者版本直接是一个 * 是什么意思呢?

这就是 npmSemantic versioning,简称 Semver,中文含义即语义化版本规则。这些特殊符号被称作 semver 通配符

在开发中我们有过这样的经验:有时候依赖的包升级后大改版,之前提供的接口不见了,这对使用者的项目可能造成极大的影响。

因此我们在声明对某个包的依赖时需要指明是否允许 update 到新版本,什么情况下允许更新,npm 包提供者需要了解的 Semantic versioning 语义化版本规则。版本由三部分组成:X,Y,Z,分别是主要版本,次要版本和补丁版本。

作为包的开发者,应该从 1.0.0 版本开始。以后要升级版本应该遵循以下标准:

  • 补丁版本:解决了 Bug 或者一些较小的更改,增加最后一位数字,比如 1.0.1
  • 小版本:增加了新特性,同时不会影响之前的版本,增加中间一位数字,比如 1.1.0
  • 大版本:大改版,无法兼容之前的,增加第一位数字,比如 2.0.0

作为包的使用者,可以针对自己的需要填写依赖包的版本规则:

作为使用者,我们可以在 package.json 文件中写明我们可以接受这个包的更新程度(假设当前依赖的是 1.0.4 版本):

如果不打算更新任何版本,也就是只使用固定版本的包,可以这么写:

  • 1.0.4

如果只打算接受补丁版本的更新(也就是最后一位的改变),就可以这么写:

  • 1.0

  • 1.0.x

  • ~1.0.4

如果接受小版本的更新(第二位的改变),就可以这么写:

  • 1

  • 1.x

  • ^1.0.4

如果可以接受大版本的更新(自然接受小版本和补丁版本的改变),就可以这么写:

  • x

package-lock.json文件

背景

package.json 中定义这样的依赖项的真正好处是,任何有权访问 package.json 的人都可以创建一个包含运行应用程序所需模块的依赖项文件夹,但是让我们来看看事情可能出错的具体方式。

假设我们创建了一个将使用 vue 的新项目。我们安装了 vue

1
$ npm install vue --save

在安装时,vue 的最新版本是2.5.17,所以 “vue”:“^2.5.17” 作为我的package.json中的依赖项添加,并且我的电脑安装了确切的版本。

现在也许明天,vue 的维护者会发布 bug 修复或新特性的添加,发布后的最新版本变为2.6.0。 显然,根据版本号的变化,我们知道,这是新增了新的特性了,vue 的这个版本添加 v-slot 指令,用于逐步替换掉 2.5.x 版本的 slot-scope 特性,并且计划在 3.x版本完全去除 slot-scope 特性。

然后,如果有人想要为我的项目做贡献,他们会克隆它,然后运行npm install。因 2.6.0是具有相同主要版本的更高版本,所以为它们安装。 我们都安装 vue ,但我们却是不同的版本。

从理论上讲,它们应该仍然是兼容的,但也许 bugfix 会影响我们正在使用的功能,而且当使用vue版本2.5.172.6.0运行时,我们的应用程序可能会产生不同的结果。(虽然 vue 的这次版本更新兼顾到了 2.5.x 的版本,你依然可以使用 slot-scope 特性,但其他库可就不一定了)。

目的

package-lock.json 的目的是避免上述情况,其中从同一 package.json 安装模块会导致两种不同的安装。 在 npm 版本 5.x.x 中添加了 package-lock.json,因此如果你使用的是主要版本 5 或更高版本,除非禁用它,否则它会自动生成。

内容结构

package-lock.jsonpackage.json 中列出的每个依赖项的大型列表,应安装的特定版本,模块的位置(URI),验证模块完整性的哈希,它需要的包列表 ,以及依赖项列表。 让我们来看看 vue 的列表是什么:

1
2
3
4
5
"vue": {
"version": "2.5.17",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.5.17.tgz",
"integrity": "sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ=="
"requires": {},

npm(^5.x.x) 之后的做法,npm 使用package-lock.json,而不是使用package.json 来解析和安装模块。也就是说,当你使用 npm install 安装所有项目依赖时,npm 会根据 package-lock.json 文件中各模块包的 version 字段来安装特定版本的依赖包。

因为 package-lock.json 为每个模块及其每个依赖项指定了版本,位置和完整性哈希,所以它每次创建的安装都是相同的。 无论你使用什么设备,或者将来安装它都无关紧要,每次都应该给你相同的结果,这非常有用。

如果我们想要更新 vue 的版本怎么办呢?常规操作是:

  • npm outdated vue 查看 vue 的最新变动。
1
2
3
4
5
6
$ npm outdated vue

# 下方为打印结果
Package Current Wanted Latest Location
vue 2.5.17 2.6.9 2.6.9 demo

可以看出,vue 的最新版本变为 2.6.9 了。于是我们可以使用 npm update 命令来更新 vue

1
2
3
4
5
$ npm update vue

# 结果
+ vue@2.6.9
updated 1 package in 1.8s

现在 package-lock.json 文件中 vue 的变化。

1
2
3
4
5
"vue": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.9.tgz",
"integrity": "sha512-t1+tvH8hybPM86oNne3ZozCD02zj/VoZIiojOBPJLjwBn7hxYU5e1gBObFpq8ts1NEn1VhPf/hVXBDAJ3X5ljg=="
},

没错!她现在已经被锁定为 2.6.9 的版本了。此时,你应该将你的 package-lock.jsonpackage.json 变更提交到代码控制仓库,这时,当你的同事 pull 代码的时候,发现这两个文件发生了变化,那么他应该重新执行 npm install ,已确保他项目的 node_modules 中的所有依赖与其他开发人员一致。

能够触发 package-lock.json 文件变更的操作。

  • 手动修改 package.json 中依赖包的版本号,然后执行 npm install (不推荐)。
  • 使用 npm update [package name[@version]] 来更新包。(推荐)