前言
Hello 大家好!我是壹甲壹!
对于平常只会用到简单的 npm 命令的我来说,面试的时候就被考官按在地上摩擦过
- 请问
package.json
中版本~1.2.3
和^1.2.3
有什么区别? - 请问执行
npm install
时,是怎么安装依赖包的?相同的依赖包怎么处理?
面试结果可想而知,于是便有了这篇文章,好好总结下 npm 相关的知识啦
一、npm 安装
当我们在安装 node
后,npm
也会一起安装好,此时需要另外两个包来协助开发
nvm
管理 node 版本nrm
管理 npm 的源,国内一般都使用taobao
或cnpm
源
详细安装信息,请参考 Node.js的3m安装法
二、npm 命令
2.1 npm init
npm init
命令用来初始化项目
- 直接执行
npm init
, 会依次询问name, version, description
等配置信息,比较麻烦 - 添加
--yes
或-y
后缀,直接生成package.json
文件,配置信息使用默认值 (默认值在npm config
中定义), 可手动修改
对于每个项目而言,例如 author.name
, author.email
都是固定的,可以给这些字段设置默认值
// 查看所有配置信息
npm config list --json
// 设置默认值
npm config set init.author.name "userName"
npm config set init.author.email "userEmail"
接下来,执行 npm init -y
, 生成的 package.json
文件如下
{
"name": "pkg-a",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "userName <userEmail>",
"license": "ISC"
}
2.2 npm install
npm install
命令用来全局安装或本地安装第三方的依赖。npm install
简写为 npm i
2.2.1 global
npm i create-react-app -g
会进行全局安装,安装成功后,可在终端任何地方使用 create-react-app
命令。
- 全局安装路径
执行 npm root -g
输出全局安装路径为
/Users/xxx/.nvm/versions/node/v14.2.0/lib/node_modules
(Tip: 此时可以看出 nvm
管理node 版本时,每个版本都有对于的 node_modules
)
- 全局使用
之所以能够全局使用,是因为将 xxx/v14.2.0/lib/node_modules
目录下每个依赖的可执行文件软链接 到 xxx/v14.2.0/bin
目录下。
每个依赖包的可执行文件在其 package.json
文件中的 bin
字段指定
{
"name": "create-react-app",
"bin": {
"create-react-app": "index.js"
}
}
那么,在 xxx/v14.2.0/bin
目录就会创建 create-react-app
命令,在执行该命令时,就是在执行 index.js
文件
2.2.2 local
npm i moment --save
会在当前项目进行安装,安装成功后,需要在项目中引入后,才能使用
依赖来源
npm i
默认会从 npm registry
上去下载依赖的模块 (使用 nrm
切换),除此之外,还有哪些安装途径呢?
- from folder
可以从本地的文件夹中安装 pkg。本地创建
pkg-a
文件夹,并通过npm init -y
进行初始化,接下来执行npm i ~/pkg-a
就可以安装了,成功之后pkg-a
的值为相对链接的 file 协议地址

- from tarball
对于👆的本地模块
pkg-a
, 执行npm pack
命令就可对其打包了接下来,就可以执行
npm i ~/pkg-a/pkg-a-1.0.0.tgz
安装模块了,安装成功后pkg-a
的值为相对链接的 file 协议地址
npm install tarball
比较适合离线安装包或者包不想发布的 npm 源上,但需要手动维护
- from http(s) url
前面介绍的是本地包,对于已经发布的依赖,例如
react
,使用npm view react
可以查看最新版本的 react 压缩包地址
此时可以直接使用
npm install [https://r.cnpmjs.org/react/download/react-16.13.1.tgz](https://r.cnpmjs.org/react/download/react-16.13.1.tgz)
来安装 react
。安装完成后,在 package.json
中 react
值为 https 地址
npm install http(s)
比较适合不想发布的 npm 源上,但需要别人也能安装
- from git
我们还可以从 git 上安装,还以 react
为例,执行 npm i [https://github.com/facebook/react.git](https://github.com/facebook/react.git)
来安装
以上就是其它的几种 npm install
方式,在日常开发中还是推荐从 npm 源上面安装依赖
依赖分类
对于不同的依赖功能不同,常见会将依赖分成 开发环境依赖 和 生产环境依赖,例如 babel
属于开发依赖,而react
就属于生产依赖了。在 npm install
时可以添加不同的后缀来区分依赖,--save (-S)
表示生产依赖,下载的依赖会保存到 package.json
的 dependencies
字段中,而 --save-dev (-D)
表示开发依赖,会保存到 devDependencies
字段中。
在 npm5
中,不添加后缀,下载的依赖默认会保存到 dependencies
除去 -S -D
后缀,npm install
还存在以下后缀
-P
:--save-prod
, 当保存到dependencies
字段中, 当不存在-D
或-O
时,-P
就是默认值-O
:--save-optional
, 保存到optionalDependencies
字段中--no-save
, 将不会进行保存-B
:--save-bundle
, 不仅保存到dependencies
字段,还会保存到bundleDependencies
列表中 (可选)-E
:--save-exact
, 不仅保存到dependencies
字段,还会锁定pkg
的准确版本,而不是使用一个范围 不加-E
,版本^16.13.1
是一个范围,表示的是>=16.13.1 < 17.0.0
这个范围加上
-E
, 就会锁定react
的版本
(Tips: 关于 dependencies
这些字段,以及 ^16.13.1
表示的具体版本范围,后续 package.json
中会讲解)
安装位置
本地安装是,所有的依赖都会放到 ./node_module/
文件夹下面,同时每个依赖的可执行文件都会软链接到 ./node_module/bin/
目录下,这样就可以在 package.json
中设置 script
时使用到这些命令,如
2.2.3 How to Work?
当仅执行 npm install
时,npm 会根据 package.json 来安装项目依赖,到底是如何工作的呢?
这部分请参考 npm install 如何工作 - node_modules 目录结构
总结如下,若想实践,可通过下面 Tips
来切换 npm
版本
- **npm 2 ** 递归安装依赖
缺点:重复安装相同版本的依赖包
Tips: 执行 nvm install 4.2.0
后,node
版本 4.2.0
,npm
版本 2.14.7
- npm 3 扁平化管理, 在安装时遍历整个依赖树,计算出最合理的文件夹安装方式, 避免重复安装
Tips: 执行 nvm install 6.9.0
后,node
版本 6.9.0
,npm
版本 3.10.8
- npm 5
增加 package-lock.json 文件, 锁定依赖版本,确保任何环境运行 npm install 得到相同的 node_modules 结 构。禁用命令 npm config set package-lock false
2.3 npm view(info)
npm view(info) xxx
,查看包 xxx 的一些信息,最新稳定版本,在线压缩包地址,依赖等信息。
2.4 npm home(docs)
npm home(docs) xxx
,都会打开在线 github 地址,前提需要在包 xxx 的 package.json
文件中设置 homepage 字段
2.5 npm repo(bugs)
npm repo(bugs)
xxx, 跳转到对应的 github repo
页面,前言也是需要配置
2.6 npm ls
显示依赖 tree
三、package.json
一个项目的全部信息都在 package.json 文件中了,具体看下有哪些属性吧
3.1 必备属性
如果该项目打包发布到 npm 源上,那么 name 和 version 两个字段是必须的
3.2 version
版本号必须能够被 node-semver 正确解析。
3.2.1 版本号
标准版本号格式为: X.Y.Z , 有下面几点说明
- X.Y.Z 均为非负整数,X: 主版本号、Y:此版本号、Z:修订号。每个元素依次递增
- 任何修改都要用新的版本号发布
- 当 X 为 0 时(0.y.z),此时属于开发初期,任务公共API 都可能改变,属于非稳定期。1.0.0 用于界定公共 API 的形成
- 修改 Z, 必须在做了向下兼容的修正时才递增,例如 修复 bug
- 修改 Y, 必须在有向下兼容的新功能时才递增,当 Y 修改后,Z 必须归 0
- 修改 X, 必须在有任何不兼容的修改被加入公共 API 时递增,当 X 修改后, Y, Z 必须归 0
**
3.2.2 预发版本号
eg: 2.1.0-beta.1
- alpha(α):预览版,或者叫内部测试版
- beta(β):测试版,或者叫公开测试版
- rc(release candidate):最终测试版本
预发版本表明当前版本并非稳定,可能不符合要求。
3.2.3 版本号优先级
版本优先级也就是指版本的高低
- 首先记住
2.1.0-beta
的优先级低于2.1.0
,先有预发,再有稳定; - 对于正常版本格式
X.Y.Z
,都是数值,比较每一位大小就OK; - 而对于预发版本,有数字比数值大小,非数字比较每个字符的 ASCII 大小;
所以: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
3.2.4 范围版本号
(1)运算符:> , >=, <, <=
- 正常:
>= 1.2.7
, 那么匹配的版本可以是1.2.7
,1.2.8
,3.4.2
- 预发:
>1.2.3-alpha.3
,那么匹配的版本的X.Y.Z
必须是1.2.3
(2)字符范围:X.Y.Z - A.B.C
1.2.3 - 2.3.4
, 那么匹配的版本范围是>=1.2.3 <=2.3.4
1.2 - 2.3.4
,第一个版本缺失,会进行补零,所以匹配的版本范围是>=1.2.0 <=2.3.4
1.2.3 - 2.3
,第二个版本缺失,会要求匹配的版本的X.Y
两位不得大于 2.3,也就是< 2.4.0
, 所以版本范围是>=1.2.3 <2.4.0
,
(3)X-Range: 1.2.X , 1.2.x, 1.2.*
*
, 匹配任意版本,也就是>= 0.0.0
1.2.x
,匹配的版本 X.Y 两位不变,所以版本范围是>=1.2.0 < 1.3.0
1.x
,匹配的版本 X 位不变,所以版本范围是>=1.0.0 < 2.0.0
(4)Tilde Ranges : ~
如果Y
被指定,则允许Z
被改变;如果没有,允许Y
被改变
~1.2.3
,Y
被指定为 2, 那么匹配的范围为>=1.2.3 <1.3.0
~1.2
,Y
被指定为 2, 那么匹配的范围为>=1.2.0 <1.3.0
~1
, 没有指定 Y, 那么 Y 可以被改变,匹配的范围为>1.0.0 <2.0.0
(5)Garet Range: ^
匹配的版本,会保证最左边的第一个非 0 位不修改
^1.2.3
, 最左边非 0 位为 X 位,所以匹配的范围为>=1.2.3 <2.0.0
^0.2.3
, 最左边非 0 位为 Y 位,所以匹配的范围为>=0.2.3 <0.3.0
^1.2.x
, 当版本缺失,缺失位会降到 0 ,所以匹配的范围位>=1.2.0 <2.0.0
3.3 dependency
3.3.1 dependencies
生产环境依赖,对应 npm install
时后缀 -S
3.3.2 devDependencies
开发环境依赖,对应 npm install
时后缀 -D
3.3.3 peerDependencies
同等依赖,如果安装了依赖包 pkg-a, 最好也安装下 pkg-a 对应的依赖
// pkg-a package.json
{
"peerDependencies": {
"jquery": "2.2.0"
}
}
上面表示 pkg-a 的运行依赖于 jquery,所以当 npm install pkg-a 时,控制台会提示以下信息
在 npm 2.x 版本时,peerDependencies 会被强制安装,3.0 版本后不被强制安装
3.3.4 bundleDependencies
捆绑依赖, 也叫 bundledDependencies
,属性值是个数组
// pkg-a package.json
{
"bundledDependencies": [
"moment"
]
}
当 npm pack
时会将捆绑依赖一同打包,在安装 tgz
压缩包时,捆绑依赖也会被安装。但注意捆绑依赖并没有指定版本号
3.3.5 optionalDependencies
可选依赖,属性值为对象。当项目依赖 pkg A, 但 pkgA 安装失败或者找不到并不会影响项目的运行,就可以在 optionalDe.... 中指定 pkgA 了
3.4 homepage
指定项目主页,设置后可通过 npm docs(info) xxx
打开
3.5 bugs
指定项目 issues
页面, 设置后通过 npm bugs xxx
可打开
3.6 license

3.7 main
指定项目入口文件,当 require(xxx)
时,返回的就是入口文件导出的内容
3.8 bin
当 pkg 中的可执行文件需要安装到 PATH
中,可在 bin 中指定。以 http-server
为例
"bin": {
"http-server": "./bin/http-server",
"hs": "./bin/http-server"
}
当 http-server
全局安装,会将执行文件软连接到 xxx/node/v14.2.0/bin/http-server
中,本地安装就会链接到 ./node_module/bin/http-server
中。
可执行文件需要在文件顶部增加 #!/usr/bin/env node
指定 node 执行
3.9 config
设置 config 可通过 npm_package_config_xxx
来获取,但同名变量会被本地 npm config
所覆盖
3.10 script
这部分请参考 阮一峰 npm scripts 使用指南
四、npx
npx 命令是 npm v5.2
之后引入的新命令,npx 可以帮我们直接执行 node_modules/.bin
文件夹下的文件
- 执行脚本
npx webpack
这样就省略了配置scripts
脚本
- 避免安装全局模块
全局安装的模块会带来很多问题,例如:多个用户全局安装的模块版本不同
npx create-react-app react-project
我们可以直接使用 npx
来执行模块,它会先进行安装,安装执行后会将下载过的模块删除,这样可以一直使
最新版本
五、npm 发布
npm 包的发布比较简单
- 首先使用 nrm 切换到 npm 官方源
- 使用
npm login
进行登录(提前注册) npm publish
(确保 name 是唯一值)- 对于不想打包的可在
.npmignore
文件中进行忽略
六、参考链接
本文使用 mdnice 排版