从开发一个简单的天气查询命令行工具来认识包与NPM

本文介绍Node.js中模块的概念及其与CommonJS规范的关系,通过实战案例展示如何利用NPM进行模块管理,包括创建、发布及安装自定义命令行工具。

众所周知,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自身实现的一个方法)方法来实现模块之间的互相依赖。当我们需要实现某个功能时,可能需要开发不同的模块,这些模块通过相互引用,最终实现一个完整的功能,这些模块组合在一起,就形成了--包。原理如图

到这里,我们对包和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 指令跳过这些步骤,直接采用默认配置 输入基本包信息后,大致信息如下

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

{
  "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"
}
复制代码

至此,我们已经准备好开发的前期准备工作了,回到我们的需求,我们需要做一个天气查询命令行工具,那首先要有一个规则,这里我把规则定位,输入命令,返回城市天气,命令带上的参数为城市名字,如果没有参数,则根据ip地址判断地理位置返回天气,还需要一个天气查询的接口,简单效果大概如下

// 命令输入,参数为北京
weatherzyang 北京

// 返回结果
天气:晴
温度: 24-30度
复制代码

到这里有个疑惑,我如何在node端输入参数呢?这里就要用到node的一个属性,process.argv,先新建一个index.js文件,看看这个属性是什么,编辑index.js,内容如下

console.log(process.argv)
复制代码

通过node命令执行index.js

node index.js
复制代码

结果如下

再次通过node命令执行index.js,这次我们加上一个参数

node index.js 北京
复制代码

结果如下

可以看出来,我们输出的参数被打印了出来了,第一个问题解决。

第二个问题,我需要调用借口,那我需要发出请求,获取数据,那如何获取呢?难道需要我自己开发出一个请求模块吗?当然没这个必要,NPM的强大就在这里体现出来了,我们可以通过NPM来引入可以发出请求的包,直接调用就行了。这里就引入第二个概念,通过NPM来引入包。这里我们引入axios包,具体用法可以参考axiso-npm,具体做法如下 通过npm install 命令下载包

npm install --save axios 
复制代码

安装完成后,看看package.json,多出了dependencies字段,即我们要开发的工具包的依赖

并且项目中多出了node_modules文件夹,这个文件夹就用用来存放我们引入的包啦。

这时只需要通过require()来引入axios,即可使用其功能了。 查询接口使用网络资源,具体接口规范不多做介绍。接下来继续编辑index.js

// 引用axios模块
var axios = require('axios');

// 定义查询参数
var data = {};

// 判断用户是否输入了城市参数,如果输入了参数,就给查询参数赋值
if(process.argv[2]){
	data.params = {
		city: process.argv[2]
	}
}

// 使用axios发出查询请求,具体用法可以参考npm
axios.get('此处为接口地址', data)
	.then(function(response) {
		var weather = response.data.results[0].weather_data[0];
		console.log(response.data.results[0].currentCity);
		console.log(weather.temperature);
		console.log(weather.weather + ',' + weather.wind);
	})
	.catch(function(err){
		console.log(err);
	})
复制代码

分别不输入参数,输入参数执行index.js,效果如下

至此,一个简单的查询天气功能就做好啦,但是还有点问题,我们要做的是一个命令行工具啊,为什么还要繁琐的去输入node index.js 这样的命令啊,能再简单点吗?当然可以,还记得前面介绍的package.json配置参数吗,package.json的参数非常多,要实现命令行工具的简化,这里需要用到bin的配置。 编辑package.json,新增bin配置

{
  "name": "weatherzyang",
  "version": "1.0.0",
  "description": "阳哥天气查询demo",
  "main": "index.js",
  "bin": {
    "weatherzyang": "./index.js" // 直接通过weatherzyang 命令来执行查询天气的功能
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "weather"
  ],
  "author": "zyang",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.16.2"
  }
}
复制代码

配置好bin字段后,通过npm install -g package_name(此处-g表示全局安装) 的方式把我们发布的包全局安装,就能在命令行中直接执行weatherzyang使用

再修改一下index.js文件,在文件开头加一句声明

#!/usr/bin/env node
复制代码

这句话的意思就是声明脚本执行环境为node,因为我们开发的就是node命令行工具啊。。

一切都完成后,进入下一步,发布我们的工具到npm

npm login //输入账号密码登陆
复制代码

登陆后执行publish命令

npm publish
复制代码

至此,我们的包就发布到npm上去了,当别人需要用这个包的时候,只需要install下来即可,操作如下 全局安装发布的包,包名字为weatherzyang

npm install -g weatherzyang
复制代码

执行bin命令

weatherzyang
复制代码

效果如下,大功告成,有兴趣的同学也可以去下载这个包查看源码(可能是开了翻墙,导致默认地点竟然是徐州。。。)

另外还漏掉一个知识点,dependencies和devDependencies的区别,这两个参数都是描述包的依赖,前者为包实现功能所需的依赖,而后者即使开发包的时候所需的依赖,一般后者的依赖都是一些测试工具,仅用在开发阶段调试使用,项目上线后这些依赖对用户来说是不必要的,用户通过npm install来载入依赖的时候只会读取dependencies的依赖,而忽略devDependencies的依赖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值