包与NPM

149 阅读5分钟

众所周知,node的出现使的前端人员可以在服务器端编写javascript代码,也使前端的范围不仅仅是局限在浏览器端。而node所遵循的CommonJs规范也让javascript能够像其他语言(比如java,python)以模块化的形式开发(当然,在浏览器端目前也可以通过三方工具实现模块化规范,比如require.js,sea.js)。

node组织了自身的核心模块,也使得第三方文件模块可以有序地编写和使用。但是在第三方模块中,模块和模块之间仍然是互相独立的,他们相互之间不能互相饮用。在模块之外,包和NPM则是将模块联系起来的一种机制。

当提到模块时,不得不再回到CommonJs规范,npm可以理解成一个联系模块与模块的纽带,而其中的运作方式,都是基于CommonJs规范。其核心思想可以总结为一句话--“文件即模块” 举个简单的例子

// 定义一个cat模块,文件名cat.js
var cats = ['小猫1','小猫2','小猫3'];
module.exports = cats;

可以看到,在这个模块文件中,定义了一个数组,通过module.exports将这个数组暴露出去,这样就形成了一个模块,而其他模块想要使用这个模块时,只需通过require方法去引用就行了。

// 引用模块为main.js
// 这里引用的所写的是相对地址
var cat = require('./cat')
console.log(cat)

通过node命令执行main.js

node main.js

//输出 ['小猫1','小猫2','小猫3']

通过这个简单的例子可以看到,node遵循的CommonJs规范使其处理不同的文件时都把它们作为一个模块对待,而模块的引用则是通过require(这是node自身实现的一个方法)方法来实现模块之间的互相依赖。当我们需要实现某个功能时,可能需要开发不同的模块,这些模块通过相互引用,最终实现一个完整的功能,这些模块组合在一起,就形成了--包。原理如图

Paste_Image.png

到这里,我们对包和NPM的概念有了一个初步的概念,包是模块的集合,NPM是包管理工具。

我们已经知道了包是由一组相互依赖的模块组成的,当我们要使用一个包时,我们如何知道这个包的信息呢?这就引入到下一个概念:CommonJs包规范。

CommonJs包规范包括两方面:包结构 和 包描述文件 包结构,即包的文件结构,完全遵循CommonJs包规范的包目录应该包含如下文件:

package.json //包描述文件
bin //用于存放可执行二进制文件的目录
lib //用于存放javascript代码的目录
doc //用于存放文档的目录
test //用于存放单元测试用例的代码

包结构不做过多介绍,接下来着重介绍包描述文件package.json

包描述文件用于表达非代码相关的信息,位于包的根目录下,是包的重要组成部分,NPM的所有行为都与包描述文件的字段息息相关。 我们以大名鼎鼎的express的包描述文件为例,来一探package.json的秘密~(实际文件内容很多,此处仅截取一些典型的内容做示例)

"name": "express", //包名字
"description": "Fast, unopinionated, minimalist web framework", //包描述
"version": "4.15.3", // 包版本号
"keywords": [
    "express",
    "framework"
  ], // 包关键字,可以通过这些关键字在npm中搜索到
"maintainers": [
    {
      "name": "dougwilson",
      "email": "doug@somethingdoug.com"
    }
  ], // 维护人员名单
"contributors": [
    {
      "name": "Aaron Heckmann",
      "email": "aaron.heckmann+github@gmail.com"
    },
    {
      "name": "Ciaran Jessup",
      "email": "ciaranj@gmail.com"
    }
  ], // 贡献者
"bugs": {
    "url": "https://github.com/expressjs/express/issues"
  }, // 提交bug的地址
"license": "MIT",  // 当前包所使用的许可证列表
"repository": {
    "type": "git",
    "url": "git+https://github.com/expressjs/express.git"
  }, // github仓库地址
"dependencies": {
    "accepts": "~1.3.3",
    "array-flatten": "1.1.1"
  }, // 使用当前包所需要依赖的包的列表,该属性十分重要,后面详细介绍
"homepage": "http://expressjs.com/", //包的官网
"engines": {
    "node": ">= 0.10.0"
  }, // 支持的javascript引擎列表
"devDependencies": {
    "after": "0.8.2",
    "body-parser": "1.17.1"
  }, // 开发依赖包列表,有别与dependencies
"scripts": {
    "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
    "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
    "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
  } // 脚本说明对象,后面会介绍到

到这里,已经大概对包规范的概念有了一些了解,包规范的定义可以帮助Node解决依赖包安装的问题,而NPM正是基于该规范的实现。我们在安装好node的时候,NPM就作为一个附带内置工具包含在内,可以直接使用。

接下来,开发一个简单的node天气查询命令行工具,来看看NPM的运用。

新建一个文件夹,我们的源文件就放在这里

mkdir weatherquery && cd weatherquery

第一个npm命令,初始化

npm init

输入这个指令后,会要求我们输入一些关于这个包的一些基本信息,也可以通过 npm init -y 指令跳过这些步骤,直接采用默认配置 输入基本包信息后,大致信息如下

Paste_Image.png

确认后输入yes按回车,再回到我们的根目录下,可以看到,多出了package.json文件,里面的内容就是我们配置的信息

Paste_Image.png

{
  "name": "weatherzyang",
  "version": "1.0.0",
  "description": "阳哥天气查询demo",
  "main": "index.js", //入口文件为index.js
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "weather"
  ],
  "author": "zyang",
  "license": "ISC"
}