简单的说 Node.js 就是运行在服务端的 JavaScript。它的优势是基于Google的V8引擎(执行速度非常快、性能非常好、社区活跃),更具诱惑力的是采用node+mongodb开发中小型网站速度更快(据说成本更低)。
目录
1.1. Node简介
1.2. 下载安装
1.3. 模块机制
1.4. 异步I/O
1.5. 异步编程
1.6. 核心API
1.6.1. Event
1.6.2. HTTP
1.6.3. I/O
1.7. 全局对象&全局变量
1.8. util模块
1.9. assert断言
1.10. npm
1.10.1. package.json
1.10.2. 命令
1.10.3. 远程镜像
1.11. Cluster模块
1.12. PM2模块
https://nodejs.org/zh-cn/download/releases/
events是Node.js最重要的模版,原因是Node.js本身架构就是事件式的,而它提供了唯一的接口。所以开成Node.js事件编程的基石。events模块不仅用于用户代码与Node.js下层事件循环的交互。还几乎被所有的模块依赖。
events模块只提供了一个对象(events.EventEmitter)。EventEmitter的核心就是事件发射与事件监听器功能的封装。EventEmitter的每个事件由一个事件或若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter支持若干个事件监听器。当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
常用API的方法:
(1)EventEmitter.on(event,listener)为指定事件注册一个监听器,接受一个字符串event和一个回调函数listener
(2)EventEmitter.emit(event,[arg1],[arg2]....)发射event事件,传递若干可选参数到事件监听器的参数表
(3)EventEmitter.once(event,listener) 为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。
(4)EventEmitter.removeListener(event,listener)移除指定事件的某个监听器,listener必须是该事件已经注册过的监听器。
(5)EventEmitter.removeAllListeners([event])移除所有事件的所有监听器,如果指定event,则移除指定事件的所有监听器。
事件
1.普通事件的使用
//声明事件对象`
var EventEmitter=require('events').EventEmitter;
var event=newEventEmitter();
//注册事件
event.on('some_event', function() {
console.log('这是一个自定义的事件');
});
//触发事件
setTimeout(function() {
event.emit('some_event');
}, 1000);
console.log("event...")
2.Node.js的事件循环机制
(1)Node.js在什么时候进入事件循环呢?
Node.js程序是由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数。
(2)如何使用自定义事件呢?
事件的回调函数在执行的过程中,可能会发出IO请求或直接发射(emit)事件,执行完毕后再返回事件循环。
回调函数
异步式读取文件:
var fs=require('fs');
fs.readFile('file.txt', 'utf-8', function(err, data) {
if (err) {
console.log(err)
} else {
console.log(data)
}
});
console.log('end.')
同步式读取文件:
var fs=require('fs');
var data=fs.readFileSync('file.txt', 'utf-8');
console.log(data)
console.log('end.')
分析:
调用时所做的工作知识将异步式IO请求发送给了操作系统,然后立即返回并执行后面的语句,执行完以后进入事件循环监听事件,当fs接受到IO请求完成的事件时。事件循环会主动调用回调函数完成后续工作。同步则是阻塞等待完成后,继续执行。
Node.js 的核心功能之一就是作为Web 服务器,这是这个系统的一个重要部分,所以当 Ryan Dahl 发起此项目时,他为 V8 重写了 HTTP 模块,使其能够非阻塞运行。虽然最开始的 HTTP 实现已经蜕变了许多,API 和内部实现不断升级,但核心操作还是保持不变的。Node 实现的 HTTP 模块是非阻塞的,且速度很快,其中许多代码已经从 C 迁移到了 JavaScript。HTTP 使用的模式在 Node 里很常见。父工厂类提供了很容易创建新服务器的方法。http.createServer() 方法为我们提供了构建新的 HTTPServer 类的实例。我们可以为新的类定义 Node 接受到 HTTP 请求时的操作。HTTP 模块及其他 Node 模块还有一些共性的地方,比如 Server 类能够触发的事件,还有传给回调函数的数据
结构。了解这 3 种类型有助于你更好地使用 HTTP 模块。
Http服务器
var http=require('http');
var server=http.createServer();
var handleReq=function(req, res) {
res.writeHead(200, {});
res.end('hello world');
};
server.on('request', handleReq);
server.listen(8125);
console.warn("localhost is running")
Http客户端
如果你想向远程服务器发起 HTTP 连接,Node 也是很好的选择。Node 在许多情下都很适合使用,如使用 Web service,连接到文档数据库,或是抓取网页。你可使用同样的 http 模块来发起 HTTP 请求,但应该使用 http.ClientRequest 类该类有两个工厂方法:一个通用的方法和一个便捷的方法。
var http=require('http');
var opts= {
host:'www.baidu.com',
port:80,
path:'/',
method:'GET'
};
var req=http.request(opts, function(res) {
console.log(res);
res.on('data', function(data) {
console.log(data);
});
});
req.end()
URL
var URL=require('url');
var myUrl="http://www.nodejs.org/some/url/?with=query";
console.log(myUrl)
var parsedUrl=URL.parse(myUrl);
console.log(parsedUrl)
querystring
querystring 模块是用来处理 query 字符串的简单辅助模块。上一小节已经讨论过,query 字符串是在 URL 尾部编码过的参数。但是如果只是把它当做 JavaScript字符串来使用时,处理这些参数未免很烦琐。querystring 模块提供了从 query字符串中轻松提取对象的方法。它的主要功能有 parse 和 decode,还包括一些内部辅助函数,如 escape、unescape、unescapeBuffer、encode 和 stringify。如果你有一个 query 字符串,你可以使用 parse 来把它变成一个对象。
var qs=require('querystring');
console.log(qs.parse('a=1&b=2&c=d'))
数据流
可读的数据流API是一组方法和事件,提供了数据源在发送时访问数据块的功能。
基本上,可读数据流是与触发 data 事件相关的
var fs=require('fs');
var filehandle=fs.readFile('file.txt', function(err, data) {
console.log(data)
});
有时候我们需要等待完整的数据都可用后再进行操作,在这种情况下就会用到数据池模式(spooling pattern)。我们知道重点是不要让 Node 的事件循环阻塞,所以即使不想在接收到所有数据之前进行下一步处理,也不希望堵塞事件循环。在这种情况下,我们使用数据流来读取数据,但只有在接收到足够的内容后才使用这些数据。
var spool="";
stream.on('data', function(data) {
spool+=data;
});
stream.on('end', function() {
console.log(spool);
});
文件系统
文件系统模块显然非常有用,因为你需要它来访问磁盘上的文件。它几乎模仿了文
件 I/O 的 POSIX 风格,示例:读取并删除文件——但这是错误的(它是异步的)
var fs=require('fs');
fs.readFile('warandpeace.txt', function(e, data) {
console.log('War and Peace: '+data);
});
fs.unlink('warandpeace.txt');
//正确的是:
var fs=require('fs');
fs.readFile('warandpeace.txt', function(e, data) {
console.log('War and Peace: '+data);
fs.unlink('warandpeace.txt');
});
Buffer
浏览器需要JavaScript 来进行许多操作,但并不包括处理二进制数据。虽然说JavaScript 支持字节位操作,但它并没有二进制数据的原生表现形式。Node 带来了 Buffer 类,为你操作二进制数据弥补了短板。Buffer 是 V8 引擎上的扩展,这意味着它有其固有的一些限制。Buffer 实际上是对内存的直接分配,这意味着这多少受制于你在低级计算机语言方面的经验。
支持的编码类型:ASCII、UTF-8(默认)、UTF-16、Base64、Binary、Hex
//不支持的编码格式
console.log(Buffer.isEncoding('gbk'))
//字符串转Buffer
var bf=newBuffer('foobarbaz')
console.log(bf)
//Buffer转字符串
var foo=bf.toString()
console.log(foo)
console.log
这个简单的 console.log 命令借用了 Firefox 中 Firebug 调试器的概念,让你可以
轻松把输出打印到标准输出(stdout)
在JavaScript中,通常window是全局对象,而Node.js的全局对象是global,所有全局变量都是global对象的属性,如:console、process等。
全局变量
global最根本的作用是作为全局变量的宿主,满足以下条件成为全局变量:
1.在最外层定义的变量
2.全局对象的属性
3.隐式定义的变量(未定义直接赋值的变量)
在Node.js中不可能在最外层定义变量,因为所有用户代码都是属于当前模块的,而模块本身不是最外层上下文。
process
它用于描述当前Node.js进程状态的对象。提供了一个与操作系统的简单接口,通常写本地命令行程序的时候,会用到它。
1.process.argv是命令行参数数组,第一个元素是node,第二个元素是脚本文件名,第三个元素开始每个元素是一个运行参数。
2.process.stdout是标准输出流,通常我们使用的console.log() 其底层是用process.stdout.write();实现。
3.prcess.stdin是标准输入流,初始时它是被暂停的。要想从标准输入流读取数据,必须恢复流,并手动编写流的事件相应函数。
4.process.nextTick(callback)的功能是为事件循环设置一项任务。Node.js会在下次事件循环调响应时调用callback,ode.js适合IO密集型的应用,而不是计算密集型的应用。process.nextTick()提供了一个这样工具,可以把复杂的工作拆散,编较小的事件程一个。
function doSomething(args, callback) {
somethingComplited(args);
callback();
}
doSomething('12345', function onEnd() {
compute();
})
//如果假设compute()和somethingComplited()是两个较为耗时的函数。 以上的程序在调用doSomething时会先执行somethingComplited(args), 然后立即调用回调函数,在onEnd()中又会执行compute(),改写为:
function doSomething(args, callback) {
somethingComplited(args);
process.nextTick(callback);
}
使用process.nextTick() 后,改写后的程序会把上面耗时的操作拆分为两个事件,减少每个事件的执行时间,提高事件相应速度。
5.process还提供了process.pid、process.execPath、process.memoryUsage()等
util.log(string)
将 string 参数的内容加上当前时间戳,输出到 stdout 标准输出
require('util').log('Timestmaped message.');
输出: 14 Jun 16:02:06 - Timestmaped message.
util.inspect(object, showHidden=false, depth=2)
以字符串形式返回 object 对象的结构信息,这对程序调试非常有帮助
var util=require('util');
console.log(util.inspect(util, true, null));
util.inherits(constructor, superConstructor)
实现对象间原型继承的函数
var util=require("util");
var events=require("events");
function MyStream() {
events.EventEmitter.call(this);
}
util.inherits(MyStream, events.EventEmitter);
MyStream.prototype.write=function(data) {
this.emit("data", data);
}
var stream=newMyStream();
console.log(streaminstanceofevents.EventEmitter); // true
console.log(MyStream.super_===events.EventEmitter); // true
stream.on("data", function(data) {
console.log('Received data: "'+data+'"');
})
stream.write("It works!"); // Received data: "It works!"
断言(Assert)模块用于为应用编写单元测试,可以通过 require('assert')对该模块进行调用。
var assert=require('assert');
assert.equal(1, true, 'Truthy');
assert.notEqual(1, true, 'Truthy');
package.json位于模块的目录下,用于定义包的属性。我们可以看下 express 包位于 node_modules/express/package.json文件
Key |
描述 |
name |
- 包名。 |
version |
- 包的版本号。 |
description |
- 包的描述。 |
homepage |
- 包的官网 url 。 |
author |
- 包的作者姓名。 |
contributors |
- 包的其他贡献者姓名。 |
dependencies |
- 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。 |
repository |
- 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。 |
main |
- main 字段是一个模块ID,它是一个指向你程序的主要项目。就是说,如果你包的名字叫 express,然后用户安装它,然后require("express")。 |
keywords |
- 关键字 |
|
|
命令行 |
描述 |
npm –v |
显示版本 |
npm config list |
查看配置 npm config set prefix "D:\Program\nodejs\node_global" npm config set cache "D:\Program\nodejs\node_cache" |
npm install express
|
安装或升级express(web框架模块) 代码中:var express = require('express'); -g全局安装 --save-dev 安装本地并保存到package.json中 npm install [email protected] 指定版本 |
npm ls |
查看已安装的模块 -g 查看全局已安装的模块 |
npm uninstall express |
卸载模块 |
npm update express |
更新模块 |
npm search express |
搜索模块 |
npm init |
创建模块 |
npm publish |
发布模块 |
npm cache clear |
可以清空NPM本地缓存 |
npm unpublish |
撤销发布自己发布过的某个版本代码 |
大家都知道国内直接使npm的官方镜像是非常慢的,
l 淘宝npm镜像
搜索地址:http://npm.taobao.org/
registry地址:http://registry.npm.taobao.org/
l cnpmjs镜像
搜索地址:http://cnpmjs.org/
registry地址:http://r.cnpmjs.org/
配置npm的registry地址:
l 临时使用
npm --registry https://registry.npm.taobao.org installexpress
l 持久使用
npm config set registryhttps://registry.npm.taobao.org
npm config get registry // 配置后可通过下面方式来验证是否成功
npm info express // 或
当然你可以使用淘宝定制的cnpm (gzip 压缩支持) 命令行工具代替默认的 npm。
npm install -gcnpm --registry=https://registry.npm.taobao.org
cnpm install[name]
Node.js默认单进程运行,对于32位系统最高可以使用512MB内存,对于64位最高可以使用1GB内存。对于多核CPU的计算机来说,这样做效率很低,因为只有一个核在运行,其他核都在闲置。cluster模块就是为了解决这个问题而提出的。
cluster模块允许设立一个主进程和若干个worker进程,由主进程监控和协调worker进程的运行。worker之间采用进程间通信交换消息,cluster模块内置一个负载均衡器,采用Round-robin算法协调各个worker进程之间的负载。运行时,所有新建立的链接都由主进程完成,然后主进程再把TCP连接分配给指定的worker进程。
PM2模块是cluster模块的一个包装层。它的作用是尽量将cluster模块抽象掉,让用户像使用单进程一样,部署多进程Node应用。
好了,掌握了这一篇我们可以了解下一篇《常用node组件》