首页 > 编程笔记 > JavaScript笔记
NPM包管理器简介(非常详细)
早期的浏览器 JavaScript 中并不存在标准库的概念,甚至于在设计 JavaScript 时连模块化的概念都没有,JavaScript 语言无非是嵌入在浏览器里的一小段代码片段,整个 JavaScript 语言只包含一些基础原生对象和类型,更多的对象和 API 取决于其所在的浏览器,在 JavaScript 里只能调用浏览器已经封装好的接口。
因此,模块化在当时的 JavaScript 并没有什么用处和价值,所以,我们可以看到在 Node.js 之前 JavaScript 缺少这些功能。
具体来说就是:
显然致力于将 JavaScript 应用到服务器上的 Node.js 不能接受这样的 JavaScript 现状,而解决上面这一系列问题的是 CommonJS(http://www.commonjs.org)规范的出现。
CommonJS 制定了解决上面这些问题的一系列标准规范,其目标是构建 JavaScript 在包括 Web 服务器、桌面、命令行工具及浏览器在内的各方面的统一生态系统,而Node.js就是这些规范的一种实现,Node.js 自身实现了 require 方法来引入各种模块
Node.js 通过 export 方法在模块内部向外暴露接口,然后通过 require 方法来加载模块。
而 Node.js 包管理器(Node.js Package Manager,NPM)就是第三方应用库的管理工具,其官网地址为:https://www.npmjs.com/
JavaScript 的流行多少要归功于 NPM,如果没有 NPM 管理数量众多的开源软件包,也许就不会有现在的 JavaScript 与 Node.js。由 NPM 来管理和分发软件包,极大地方便了 Node.js 软件的管理,特别是海量的开源 Node.js 软件的管理。
NPM 的思想或多或少来源于 Java 世界的 Jar 包管理器,包括 Apache Maven。NPM 几乎可以说是继 GitHub 之后最成功的开源软件管理系统。
在 NPM 的网站上,用户不仅可以搜索所需要的包,而且可以查看包的流行程度和下载量,还可以给喜欢的包加上星号。当然用户也可以发布自己的包。要设计自己的包,我们首先需要了解包结构。
Node.js 作为服务器端程序自然也致力于改变这种现状。
Node.js 遵循 CommonJS 标准,而 CommonJS 标准已经定义了包的结构规范(http://wiki.commonjs.org/wiki/Packages/1.0)。NPM 则是在 CommonJS 规范的基础上,实现解决包的安装与卸载、依赖管理、版本管理等问题。
一个符合 CommonJS 规范的 NPM 包应该是如下这种目录结构:
Node.js 在加载程序过程中没有找到目标文件时,会将当前目录当作一个包来尝试加载,所以在 package.json 文件中,最重要的一个字段是 main。
以下是 Express 框架的 package.json 文件,值得参考:
NPM 在对包进行安装或者卸载的时候需要进行一些编译或者清除的工作,scripts 字段的对象指明了在进行操作时运行哪个文件,或者执行哪条命令。
以下为一个较全面的 scripts 案例:
NPM 会分析该文件夹下的 package.json 文件,然后上传目录到 NPM 的站点。用户在使用该包时,也十分简明:
如果想把某个包安装到 Node.js 的全局环境里,可以使用:
除了常用的 install 之外,还有下面这些常用命令:
某家公司发律师函电子邮件给 KIK 包的作者,声称 KIK 这个名字是他们的注册商标,KIK 包作者未理会他们的警告。接着 NPM 官方介入,修改了 KIK 包的名字。
由于这一行为没有和 KIK 包作者沟通,引起了 KIK 包作者的强烈不满。
23 日凌晨,KIK 包作者决定撤出 NPM,取消发布自己的所有模块,这些模块一共有 273 个,其中包括被广泛使用的 left-pad,导致 Babel、ReactNative、Ember 等大量工具构建失败。这几乎使全球所有的 JavaScript 开发者在那一天停摆。
该事件不仅造成大量依赖 left-pad 的软件重新修改代码,发布不依赖 left-pad 的版本,而且促使 NPM 修改包发布规则,以防止类似事件再次发生。
因此,模块化在当时的 JavaScript 并没有什么用处和价值,所以,我们可以看到在 Node.js 之前 JavaScript 缺少这些功能。
具体来说就是:
- JavaScript没有模块系统。没有原生地支持密闭作用域或依赖管理。
- JavaScript没有标准库。除了一些核心库外,没有文件系统的 API,没有 I/O 流 API 等。
显然致力于将 JavaScript 应用到服务器上的 Node.js 不能接受这样的 JavaScript 现状,而解决上面这一系列问题的是 CommonJS(http://www.commonjs.org)规范的出现。
CommonJS 制定了解决上面这些问题的一系列标准规范,其目标是构建 JavaScript 在包括 Web 服务器、桌面、命令行工具及浏览器在内的各方面的统一生态系统,而Node.js就是这些规范的一种实现,Node.js 自身实现了 require 方法来引入各种模块
Node.js 通过 export 方法在模块内部向外暴露接口,然后通过 require 方法来加载模块。
NMP 是什么
虽然 Node.js 内置的标准库已经覆盖了网络操作所需要的绝大部分功能,但是标准库仍然过于初级,并且不能满足所有的编程需要,所以有必要引入第三方库。而 Node.js 包管理器(Node.js Package Manager,NPM)就是第三方应用库的管理工具,其官网地址为:https://www.npmjs.com/
JavaScript 的流行多少要归功于 NPM,如果没有 NPM 管理数量众多的开源软件包,也许就不会有现在的 JavaScript 与 Node.js。由 NPM 来管理和分发软件包,极大地方便了 Node.js 软件的管理,特别是海量的开源 Node.js 软件的管理。
NPM 的思想或多或少来源于 Java 世界的 Jar 包管理器,包括 Apache Maven。NPM 几乎可以说是继 GitHub 之后最成功的开源软件管理系统。
在 NPM 的网站上,用户不仅可以搜索所需要的包,而且可以查看包的流行程度和下载量,还可以给喜欢的包加上星号。当然用户也可以发布自己的包。要设计自己的包,我们首先需要了解包结构。
包结构
浏览器时代的 JavaScript 缺少模块结构,更进一步的浏览器时代的 JavaScript 也缺少包管理结构。在 JavaScript 中,包的概念是一些 JavaScript 程序的集合,相类比的是 Java 中的 Jar 包,一个 Jar 包包含了多个 class 文件。Node.js 作为服务器端程序自然也致力于改变这种现状。
Node.js 遵循 CommonJS 标准,而 CommonJS 标准已经定义了包的结构规范(http://wiki.commonjs.org/wiki/Packages/1.0)。NPM 则是在 CommonJS 规范的基础上,实现解决包的安装与卸载、依赖管理、版本管理等问题。
一个符合 CommonJS 规范的 NPM 包应该是如下这种目录结构:
- 一个 package.json 文件应该存在于包顶级目录下,package.json 是 NPM 的核心文件。
- 二进制文件应该包含在 bin 目录下。
- JavaScript 代码应该包含在 lib 目录下。
- 文档应该在 doc 目录下。
- 单元测试应该在 test 目录下。
Node.js 在加载程序过程中没有找到目标文件时,会将当前目录当作一个包来尝试加载,所以在 package.json 文件中,最重要的一个字段是 main。
实际上,main 字段是 Node.js 的扩展,CommonJS 标准定义中并不包含此字段。
对于 Node.js 的 require 方法,只需要 main 属性即可。但是在除此之外包需要接受安装、卸载、依赖管理、版本管理等流程,所以 CommonJS 为 package.json 文件定义了如下一些必需的字段。字段 | 说明 |
---|---|
name | 包名,在 NPM 上是唯一的,不能有空格。 |
description | 包简介,通常会显示在一些列表中。 |
version | 版本号,是一个语义化的版本号(http://semver.org),通常为 x.y.z。该版本号十分重要,常常用于一些版本控制的场合。 |
keywords | 关键字数组,用于 NPM 中的分类搜索。 |
maintainers | 包维护者的数组,数组元素是一个包含 name、email、web 三个属性的 JSON 对象。 |
contributors |
包贡献者的数组,其中第一个元素是包的作者。 在开源社区,如果提交的“补丁”被合并进主分支的话,就应当加上这个贡献“补丁”的人,格式包含 name 和 email,例如: "contributors": [ { "name": "Jackson Tian", "email": "mail @gmail.com"}, { "name": "fengmk2", "email": "mail2@gmail.com"} ] |
bugs | 一个可以提交 bug 的 URL 地址,可以是邮件地址(mailto: mailxx@domain),也可以是网页地址(http://url)。 |
licenses |
包所使用的许可证,例如: "licenses": [ { "type": "GPLv2", "url":"http://www.example.com/licenses/gpl.html"} ] |
repositories | 托管源代码的地址数组。 |
dependencies | 当前包需要的依赖。这个属性十分重要,NPM 会通过这个属性自动加载依赖的包。 |
以下是 Express 框架的 package.json 文件,值得参考:
{ "name": "express", "description": "Sinatra inspired web development framework", "version": "3.0.0alpha1-pre", "author": "TJ Holowaychuk", ...... }除了前面提到的几个必选字段外,我们还发现了一些额外的字段,如 bin、scripts、engines、devDependencies、author,这里重点提及一下 scripts 字段。
NPM 在对包进行安装或者卸载的时候需要进行一些编译或者清除的工作,scripts 字段的对象指明了在进行操作时运行哪个文件,或者执行哪条命令。
以下为一个较全面的 scripts 案例:
"scripts": { "install": "install.js", "uninstall": "uninstall.js", "build": "build.js", "doc": "make-doc.js", "test": "test.js", "start": "index.js" }
包管理
如果你完善了自己的 JavaScript 库,使之实现了 CommonJS 的包规范,那么你可以通过 NPM 来将自己的包发布到 NPM 网站上,这样别人就可以通过 NPM 网站搜索并使用你的包了。npm publish <folder>
命令十分简单。但是在这之前需要通过 npm adduser 命令在 NPM 上注册一个账户,以便后续包的维护。NPM 会分析该文件夹下的 package.json 文件,然后上传目录到 NPM 的站点。用户在使用该包时,也十分简明:
npm install <package>
甚至对于 NPM 无法安装的包(因为某些网络原因),可以通过 GitHub 手动下载其稳定版本,解压之后通过以下命令进行安装:npm install <package.json folder>
只需将路径指向 package.json 所在的目录即可。然后在代码中通过require('package')
即可使用。如果想把某个包安装到 Node.js 的全局环境里,可以使用:
npm install -g <package>
除了常用的 install 之外,还有下面这些常用命令:
- npm test:package.json 中的测试程序对模块进行测试;
- npm ls:查看安装的模块及依赖;
- npm ls–g:查看全局安装的模块及依赖;
- npm uninstall xxx(-g):卸载模块;
- npm cache clean:清理缓存。
【拓展阅读】 一个名字引发的“血案”——NPM 删库事件
2016 年 3 月 23 日,NPM 发生了一次严重的库依赖被删除事件,事件起因是对一个名为 KIK 包的命名权的纠纷。某家公司发律师函电子邮件给 KIK 包的作者,声称 KIK 这个名字是他们的注册商标,KIK 包作者未理会他们的警告。接着 NPM 官方介入,修改了 KIK 包的名字。
由于这一行为没有和 KIK 包作者沟通,引起了 KIK 包作者的强烈不满。
23 日凌晨,KIK 包作者决定撤出 NPM,取消发布自己的所有模块,这些模块一共有 273 个,其中包括被广泛使用的 left-pad,导致 Babel、ReactNative、Ember 等大量工具构建失败。这几乎使全球所有的 JavaScript 开发者在那一天停摆。
该事件不仅造成大量依赖 left-pad 的软件重新修改代码,发布不依赖 left-pad 的版本,而且促使 NPM 修改包发布规则,以防止类似事件再次发生。