从零写一个npm包
参考(http://www.alloyteam.com/2016/03/master-npm/)
outline
- 什么是npm
- 常用命令
- 我的npm
- 写一个npm包
- npm2与npm3
什么是npm
npm(Node Package Manager)本来是Node.js的包管理工具,但随着JS这几年的蓬勃发展,现在npm已经成为了几乎所有跟JS相关的工具和软件包的管理工具,并且还在不断的发展完善中.现在最新版的Node.js的安装都会自带npm,装上Node.js就可以使用npm.
常用命令
1 | # 全局命令 |
我的npm
这里我把的npm单独拎出来将,主要是因为很多时候npm官方的registry并稳定,我们有时候需要使用国内的npm registry.这里推荐直接使用nrm一个用来管理npm registry的node包(可以替代cnpm),使用方法如下:
1 | # 全局安装nrm |
除了npm源之外,这里再提一下Node.js的版本的问题.Node.js的版本更新迭代是非常快的,可能我们需要在不同的版本之间不断切换,我们就需要一个node的版本管理工具.
这里说下我的使用习惯,在windows平台上我直接用nodist,在Linux下,我更习惯使用nvm
nodist常用命令如下(具体细节可以参考https://github.com/marcelklehr/nodist/blob/master/usage.txt):
1 | # 列举远端的nodejs/iojs的所有版本 |
写一个npm包
这里写npm包主要动机来源于,我希望做一个给torrent洗白的工具,结果发现网上需要洗白的种子并不好找.于是我就去找一些磁链网站,希望根据磁链生成torrent.简单来说magnet之于torrent的关系就像md5(‘helloworld’)之于’helloworld’的关系一样.根据磁链找到种子还是需要有人去收集,这里我们用一个叫做bt.box.n0808.com
的网站来做根据magnet生成torrent的事情.这里就不说代码具体的逻辑是什么,直接贴一个github地址,有兴趣的朋友可以自己去看.https://github.com/warjiang/magnet2torrent.
这里我主要说一下写npm包的一个基本流程,这里我尽量以代码为主注释为辅说明一下(代码什么的说起来最清楚了)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25mkdir your/npm/package
cd your/npm/package
# 这一步是可选的,但是一般开发一个插件都会开源到github上去的
git init -> git remote add origin your/github/repository/ssh_address -> git pull origin master
# 引导用户生成一个package.json
npm init
# 这里就是你需要coding的地方.
touch index.js
# 当然coding的过程你可能需要各种包,用于开发阶段的比如断言库,或者是你的包必须的
# 那么针对这两种的情况npm install的后缀参数是不一样的.
# 对于开发阶段需要的库,比如`mocha`这类的,只有开发阶段用,这样的包用`npm install --save-dev mocha`来安装
# 对于使用我们的库必须要用的包,比如`locash`这里的,这样的包用`npm install --save lodash`来安装
# ...coding....
# ...coding....
# coding完毕,这里建议写一下功能测试代码
# 建立一个test文件夹
# 这里就对应外面的功能js代码在test文件夹下建立同名测试文件
# 开发测试完毕,后面就是发布到https://www.npmjs.com了
# 这里我们首先需要到https://www.npmjs.com去注册一个帐号
# 注册完毕本地执行npm adduser输入用户名、密码就可以
# cd path/to/your/npm/package
# npm publish就可以发布了
# 中间过程如果发布不成功,很有可能是同名的package已经被别人发布过了,
# 判断同名package是否被发布过,可以到https://www.npmjs.com去search一下.
# 如果命名冲突,换个名字重新发布
到这里,基本上npm包也基本写完了.下面还要说一下npm包的文档问题.文档的话,就在开发的package下面生成一个README.md,按照markdown的语法写就成.
到目前为止我们的目录结构如下:
下面我们说说怎么更新package
更新包的话,coding完了千万不直接发布,这里我们需要修改package的version号,但这里不要直接修改.修改之前先说下npm维护package版本的规则x.y.z.
x表示主版本号,通常有重大改变或者达到里程碑才改变;
y表示次要版本号,或二级版本号,在保证主体功能基本不变的情况下,如果适当增加了新功能可以更新此版本号;
z表示尾版本号或者补丁号,一些小范围的修修补补就可以更新补丁号.
对应到npm的用法为1
2
3npm version patch <=> z++
npm version minor <=> y++ && z=0
npm version major <=> x+= && y=0 && z=0
所以说当我们对我们的package进行功能迭代之后,根据改动功能的大小,我们执行npm version <update_type>
,再执行npm publish
就是重新发布新的package.
同时注意:
如果npm包同时又是一个git仓库,在运行了npm version <update_type>
和npm publish
之后,npm会自动给git仓库打上一个跟当前版本号一样的tag,对于挂在github上的npm包很有用。
npm2与npm3
最后说说npm2与npm3的区别.
升级到npm3 npm install -g npm
npm3的出现解决了npm包目录太深的问题.相信使用过npm1或者npm2的同学都知道,node_modules太多太深了,甚至一不小心就超过windows资源管理器能处理的最长路径长度了,听起来有点拗口,说白了这时候复制粘贴删除就会报错了.而npm3将依赖模块扁平化存放了,node_modules文件夹里面子文件夹增多了,出现了很多没有通过npm install安装过的模块.npm3在安装包的时候,由于每个包和包的依赖都会去计算是否需要再安装,搜索起来确实变慢了,好在至少现在的npm3速度还是可以接受的.
假如我们写了个模块App,需要安装两个包A@1和C@1,其中A@1依赖另一个包B@1,C@1依赖B@2,用npm2和npm3安装之后的依赖图分别是这样的
npm2的安装思路就是首先安装A@1,发现A@1依赖B@1就安装B@1.在安装C@1,发现C@1依赖B@2就安装B@2.
而npm3的安装思路是首先安装A@1,没有发现A的其他版本的冲突,则直接安装在第一级目录,同时发现A@1依赖B@1,也不存在其他版本的B冲突,所以B@1就直接安装在第一级目录,然后安装C@1,没有其他版本的C冲突,直接安装在第一级目录,发现C@1依赖B@2,这里发现已经安装了B@1,就把B@2放在C@1的目录下.
现在App又需要安装一个包D@1,D@1依赖B@2.首先安装D@1,发现没有冲突的D,D@1直接安装在一级目录.D@1依赖B@2,同时已经有B@1存在于第一级目录上,所以这时B@2就安装在D@1目录下.使用npm3安装之后,包结构将变成下面这样:
接着又安装了一个E@1,依赖B@1.安装E@1时没有冲突的E,所以E直接安装在一级目录下.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.先升级A@1,首先就是移除A@1,再移除A@1的时候,由于E@1还依赖B@1,所以直接移除A@1,安装A@2,发现A@2依赖B@2,同时一级目录上还存在B@1,则把B@2安装在啊A@2目录下.升级E@1的时候,移除E@1发现B@1不再被任何包依赖,于是移除B@1.安装E@2,发现E@2依赖B@2,同时一级目录上不存在任何B的包,直接把B@2安装在一级目录上.新的包结构将变成这样:
显然,出问题了.可以看到出现了冗余,结果跟预期的不一样,既然所有对B的依赖都是B@2,那么只安装一次就够了.
npm在安装包的时候没有这么”智能”,不过npm dedupe
命令做的事就是重新计算依赖关系,然后将包结构整理得更合理.执行一遍npm dedupe
将得到: