npm 本来是 Node.js 的包管理工具,但随着 JS 这几年的蓬勃发展,现在的 npm 已经成了几乎所有跟 JS 相关的工具和软件包的管理工具了,并且还在不断发展完善中。
本文从笔者的经验,总结了 npm 安装/卸载、更新、发布这几个最主要功能的正确使用姿势和一些小技巧,顺便从官网搬来了 npm3 处理依赖的重大变化。
npm3
npm 团队已经发布了 npm3,近期有小伙伴吐槽 npm3 安装软件包的时候很慢,一开始笔者也感觉相比 npm2 慢了不少,但经过了几个版本的迭代,速度似乎又快起来了。
慢的同学是时候更新你的 npm 啦,而且之前安装进度条模糊成一坨的问题也已经修复了。
npm v3.0 安装 react 的截图
npm v3.8 安装 react 的截图
npm 提供了大量的命令,所有的命令几乎都可以通过 npm cmd [options] 的方式使用。
npm -h
学习一个命令行工具,最简单直接的方式就是查看它的用户手册,npm 提供了并不算很详细的命令行手册,可以通过 npm -h 查看(unix 用户还可以通过 man npm 查看,相对来说比 windows 详细多了),需要某个 npm 命令更详细的文档则需要通过 npm help cmd 如 npm help install 来查看,注意不是 npm install help ,这样将会安装 help 包。
另外 npm cmd -h 也是一个快速查看命令可以怎么使用和搭配哪些常用选项的方法。
npm init
说到 npm 就不得不说 package.json,每一个 npm 包都必须有一个 package.json 文件,年轻时候的我还傻乎乎的从其他地方拷贝 package.json 过来然后修改,为了自动化还写了个自动生成的脚本。
后来才发现原来 npm 自带此功能,官方原厂功能更好更强大,只需要执行 npm init 即可,以交互方式完成 package.json 的创建。
如果想生成默认 package.json,可以执行 npm init -y,连交互式界面都不会出现。
另外需要注意,npm init 的时候需要输入用户字段,如果还没有设置 npm 用户,需要通过 npm addUser 设置。
事实上,最小单位的 npm 包就是只包含一个 package.json 文件的包,这样的话 npm init 就完成了一个 npm 包的创建。
npm install/uninstall
npm install 作为 npm 最重要的功能和最常用的功能,不用过多说明,这里只介绍三个非常有用的选项--global,--save,--save-dev。
想必读者肯定知道--global 可以简写成-g,其实另外两个选项也有简写形式,--save 可以简写成-S,--save-dev 可以简写成-D,注意大写。
另外 npm install 也可以简写成 npm i,相应的卸载命令 npm uninstall 可以简写成 npm un,事实上 npm 的很多命令和选项在设计上都非常类似 unix 上的命令行功能,这里指的是命令和选项都可以极大化地简写,只要在不混淆的情况下。
npm uninstall 和 npm install 接受同样的选项和参数。
--save 的作用是在 packaje.json 的 dependencies 字段增加或者修改一个安装包和版本号名值对,--save-dev 则是修改 devDependencies,这样就不用安装了某个包之后手动修改 package.json,npm 已经帮我们把包依赖和版本管理做好了。
以安装 react 为例,
npm i react -S 将为 package.json 增加
1 2 3 |
"dependencies": { "react": "^0.14.7" } |
npm i react -D 将增加
1 2 3 |
"devDependencies": { "react": "^0.14.7" } |
npm update
假如 react@15(版本号,下同)发布了,想尝鲜的小伙伴该怎么更新呢?
首先得知道 npm 上是否已经更新,npm info react 可以查看到 react 在 npm 上发布过哪些版本以及最新的版本,但是内容太多,让人眼花缭乱,配合 grep 会好一些。
其实我们只想知道 react 最新的版本,使用 npm dist-tags ls react 直接列出 react 发布过哪些 tag,
1 2 3 4 5 |
> npm dist-tags ls react 0.10.0-rc1: 0.10.0-rc1 0.11.0-rc1: 0.11.0-rc1 latest: 0.14.7 next: 15.0.0-rc.1 |
以及这些 tag 目前最新是哪些版本,比如最常用的 latest,也是默认 tag。
next tag 已经发布了 react@15 的第一个 rc 版了,尝鲜的朋友可以试一试了。
另外一个命令 npm outdated,会检测当前安装的所有 npm 包是否有更新,并列出可以更新的包,如果没有任何输出,那么恭喜你,所有的包都是不需要更新的。
如果之前安装的 react 版本是 0.14.3,同时还安装了 redux@3.2.0,执行 npm outdated 会输出
1 2 3 |
Package Current Wanted Latest Location react 0.14.3 0.14.7 0.14.7 example redux 3.2.0 3.3.1 3.3.1 example |
这种情况则说明 react 和 redux 该更新了,更新具体某个包使用 npm update package_name 即可,npm update 则会更新所有可更新的包。
npm publish
npm 作为一个大仓库,每天都有大量的新包发布上来,发布自己的包非常容易,而且几乎零门槛,对应的发布的命令是 npm publish,但前提是你需要一个 npm 账号。
假设已经有账号了,在发布之前需要使用 npm login 进行登录,正式发布之前请先阅读以下关于版本号的介绍。
npm 包的版本号一般都是 x.y.z 的形式。
其中 x 表示主版本号,通常有重大改变或者达到里程碑才改变;
y 表示次要版本号,或二级版本号,在保证主体功能基本不变的情况下,如果适当增加了新功能可以更新此版本号;
z 表示尾版本号或者补丁号,一些小范围的修修补补就可以更新补丁号。
第一版本通常是 0.0.1 或者 1.0.0,当修改了代码,需要更新版本号重新发布到 npm,不知道的小伙伴(年轻的我)肯定会手动修改 package.json 的 version 字段,而高级的玩法是直接使用 npm version <update_type> 命令自动搞定。
详细用法可通过 npm help version 查看,这里只介绍最常用的三种。
1 2 3 |
npm version patch => z+1 npm version minor => y+1 && z=0 npm version major => x+1 && y=0 && z=0 |
三个选项分别对应三部分的版本号,每次运行命令会导致相应的版本号递增一,同时子版本号清零。
如果 npm 包同时又是一个 git 仓库,在运行了 npm version <update_type> 和 npm publish 之后,npm 会自动给 git 仓库打上一个跟当前版本号一样的 tag,对于挂在 github 上的 npm 包很有用。
npm2 & npm3
上面介绍了 npm 包安装/卸载、更新和发布,几乎能满足日常使用了,另外再搬点干货过来。
npm3 虽然慢,但解决了 windows 上 npm 包目录太深的问题,相信使用过 npm1 或者 npm2 的都知道,node_modules 太多太深了,甚至一不小心就超过 windows 资源管理器能处理的最长路径长度了,听起来有点拗口,说白了这时候复制粘贴删除就会报错了。
已经使用过 npm3 的肯定会发现,npm3 将依赖模块扁平化存放了,node_modules 文件夹里面子文件夹增多了,出现了很多没有通过 npm install 安装过的模块。
npm3 在安装包的时候,由于每个包和包的依赖都会去计算是否需要再安装,搜索起来确实变慢了,好在至少现在的 npm3 速度还是可以接受的。
按照官方文档介绍,npm3 处理模块依赖的方式跟 npm2 很不一样。
以下是从官网搬的砖
npm 的依赖
假如我们写了个模块 App,需要安装两个包 A@1 和 C@1,其中 A@1 依赖另一个包 B@1,C@1 依赖 B@2,用 npm2 和 npm3 安装之后的依赖图分别是这样的
npm3 按照安装顺序存放依赖模块,先安装 A@1,发现依赖模块 B@1 没有安装过也没有其他版本的 B 模块冲突,所以 B@1 存放在第一级目录,B@2 为了避免和 B@1 的冲突,还是继续放在 C@1 之下。
npm2 没什么好说的,来什么安装什么,根本不用理会公共依赖关系,依赖模块一层一层往下存放就是了,下面重点讲解 npm3 在这方面的改进。
现在 App 又需要安装一个包 D@1,D@1 依赖 B@2,使用 npm3 安装之后,包结构将变成下面这样
虽然 C@1 和 D@1 都依赖 B@2,但是由于 A@1 先安装,A@1 依赖的 B@1 已经安装到第一级目录了,后续需要安装的所有包 B,只要版本不是 1,都需要避免和 B@1 的冲突,所以只能像 npm2 一样,安装在相应包下面。
接着又安装了一个 E@1,依赖 B@1,因为 B@1 已经安装过,且不会有版本冲突,这时候就不用重复安装 B@1 了,包结构会变成这样
随着 App 升级了,需要把 A@1 升级到 A@2,而 A@2 依赖 B@2,把 E@1 升级到 E@2,E@2 也依赖 B@2,那么 B@1 将不会再被谁依赖,npm 将卸载 B@1,新的包结构将变成这样
可以看到出现了冗余,结果跟预期的不一样,既然所有对 B 的依赖都是 B@2,那么只安装一次就够了。
npm dedupe
npm 在安装包的时候没有这么“ 智能”,不过 npm dedupe 命令做的事就是重新计算依赖关系,然后将包结构整理得更合理。
执行一遍 npm dedupe 将得到
这才是最优且符合预期的结构,看来在每次安装/卸载了包之后最好重新执行 npm dedupe,以保证包结构是最优的。
npm3 通过将依赖模块扁平化安装,避免了冗余又解决了 windows 上一大头疼问题。
五月天 2017 年 10 月 25 日
看懂了 最近遇到个问题 npm 版本是 4 npm install 的时候 有很多关于依赖版本太低的 warning 也有很多 deduped 的依赖 这个怎么处理呢 依赖关系很复杂 如果要升级一个包 那可能有十多个包依赖它 求建议
a 2017 年 5 月 11 日
我能看懂。。
Wing刘永乐 2017 年 2 月 14 日
赞!
混在成都 2016 年 8 月 29 日
npm publish 成功之后只有一个 package.json… 这是什么问题!
wujohns 2016 年 7 月 22 日
npm3 不利于自己开发调试 npm 包
rambo 2016 年 7 月 14 日
npm 3.0 是扁平了一点,只是没有完全扁平,而且发现用了 3.x 的 NPM 安装后体积竟然更大了 -_-! 还是放弃
rambo 2016 年 7 月 14 日
在别的论坛上看到的 求验证 node_modules 长了 windows 下都删除不了
123 2016 年 5 月 24 日
为何 linux 上 npm3.9.2 还是俄罗斯方块啊?
meepo 2016 年 5 月 4 日
写的很棒。再问一下,–save-dev 和–save 除了在 package.json 中对应的字段不同,在应用上还有什么具体的不同?
shuaqiu 2016 年 5 月 16 日
我的理解是, dependence 是代码里面实际用到的依赖, 而 devDependence 是开发过程中用到的, 比如打包 gulp 这些~
cqy 2016 年 4 月 30 日
~(≧▽≦)/~赞~~~
川 2016 年 3 月 24 日
很好~保持更新, 顶你们
TAT.云中飞扬 2016 年 3 月 25 日
谢谢支持 [嘻嘻]
雁过不留声 2016 年 3 月 23 日
腾讯前端可以啊,还在持续更新,我看看淘宝的前端都已经弃更多时了。。。
TAT.云中飞扬 2016 年 3 月 25 日
我们依然坚持每个月都会有大量产出
八维熊 2016 年 3 月 21 日
顶 JJ, 但是还有 npm 的 scripts,link,通过–registry 使用镜像,加速下载啊~ 可以写成《深入浅出 npm 系列》[挤眼]
TAT.云中飞扬 2016 年 3 月 23 日
哈哈,一次写完了就弹尽粮绝了,后续再琢磨琢磨
👻 2016 年 3 月 19 日
[威武]
TAT.老教授 2016 年 3 月 16 日
怎样发布包写得简单了点
233 2016 年 3 月 16 日
赞