# 核心模块
核心模块是 Node.js 的心脏,它由一些精简而高效的库组成,为 Node.js 提供了基本的 API。
# Event 模块
Node 是事件驱动的, events 是 Node 最重要的模块.
# 事件发射器 EventEmitter
events 模块只提供了一个对象: events.EventEmitter
。
EventEmitter 的核心就 是 事件发射 与 事件监听器 功能的封装。
EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。
对于每个事件,EventEmitter 支持若干个事件监听器。当事件发射时,注册到这个事件的 事件监听器 被依次调用,事件参数 作为 回调函数参数 传递。
例子:
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) { console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) { console.log('listener2', arg1, arg2);
});
emitter.emit('someEvent', 'Garrik', 1997);
结果是:
listner1 Garrik 1997
listner2 Garrik 1997
emitter
为事件 someEvent
注册了两个事件监听器,然后发射了 someEvent
事件。运行结果中可以看到两个事件监听器回调函数被先后调用。
# 监听器的 this
当一个普通的监听器函数被 EventEmitter 调用时,标准的 this
关键词会被设置指向监听器所附加的 EventEmitter。
var event = new require('events').EventEmitter();
event.on('someEvent', function() {
// this 指向 event
console.log(this);
});
event.emit('someEvent');
可以使用 ES6 的箭头函数作为监听器。但是这样 this
关键词就不再指向 EventEmitter 实例
event.on('someEvent', () => {
// this 指向 event 所处作用域
console.log(this);
});
# EventEmitter 常用的 API
# EventEmitter.on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event
和一个回调函数 listener。
emitter.addListener(eventName, listener)
是它的别名
# EventEmitter.emit(event, [arg1], [arg2], [...])
发射 event
事件,传递若干可选参数到事件监听器的参数表。
# EventEmitter.once(event, listener)
为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。
# EventEmitter.removeListener(event, listener)
移除指定事件的某个监听器, listener
必须是该事件已经注册过的监听器。
# EventEmitter.removeAllListeners([event])
移除所有事件的所有监听器, 如果指定 event
,则移除指定事件的所有监听器。
EventEmitter.off(eventName, listener)
的别名
# EventEmitter.eventNames()
返回一个列出触发器已注册监听器的事件的数组
# EventEmitter.listenerCount(eventName)
返回正在监听指定事件的监听器的数量。
# Error 事件
EventEmitter 定义了一个特殊的事件 error. 在遇到异常的时候通常会发射 error 事件。当 error 被发射时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,抛出错误、打印堆栈跟踪、且退出 Node.js 进程。
我们一般要为会发射 error 事件注册至少一个监听器,避免遇到错误后整个程序崩溃。
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {
console.error('有错误');
});
myEmitter.emit('error', new Error('whoops!'));
// 打印: 有错误
# Fs 文件模块
fs 模块是文件操作的封装,它提供了文件的读取、写入、更名、删除、遍历目录、链接等类似标准 POSIX 函数的方式与文件系统进行交互
fs 模块中所有的操作都提供了异步的和 同步的两个版本
异步形式的最后一个参数都是完成时回调函数。 传给回调函数的参数取决于具体方法,但回调函数的第一个参数都会保留给异常。
如果操作成功完成,则第一个参数会是 null
或 undefined
当使用同步操作时,任何异常都会被立即抛出,可以使用 try/catch
来处理异常,或让异常向上冒泡。
注意,异步的方法不能保证执行顺序。
大部分 fs 操作接受字符串、Buffer、或 使用 file: 协议的 URL 对象 作为文件路径。
# 常用操作
# fs.readFile
fs.readFile(filename,[encoding],[callback(err,data)])
是最简单的读取 文件的函数。
它接受一个必选参数 filename,表示要读取的文件名。第二个参数 encoding 是可选的,表示文件的字符编码。callback 是回调函数
如果指定了 encoding,data 是一个解析后的字符 串,否则 data 将会是以 Buffer 形式表示的二进制数据。
var fs = require('fs');
fs.readFile('content.txt', function(err, data) { if (err) {
console.error(err); } else {
console.log(data);
}
});
# fs.open
fs.open(path, flags, [mode], [callback(err, fd)])
是 POSIX open 函数的封装
它接受两个必选参数,path 为文件的路径, flags 可以是以下值:
- r :以读取模式打开文件。
- r+ :以读写模式打开文件。
- w :以写入模式打开文件,如果文件不存在则创建。
- w+ :以读写模式打开文件,如果文件不存在则创建。
- a :以追加模式打开文件,如果文件不存在则创建。
- a+ :以读取追加模式打开文件,如果文件不存在则创建。
mode 参数用于创建文件时给文件指定权限,默认是 0666 (读 + 写)
# PATH 模块
path
模块提供了一些工具函数,用于处理文件与目录的路径。
path
模块的默认操作会根据 Node.js 应用程序运行的操作系统的不同而变化。 比如,当运行在 Windows 操作系统上时,path
模块会认为使用的是 Windows 风格的路径。在 Linux 系统就是 POSIX 标准风格路径。
要想在任何操作系统上处理 POSIX 文件路径时获得一致的结果,可以使用 path.posix
:
// 在 Windows 和 Mac 下 返回一样
path.posix.basename('/tmp/myfile.html');
// 返回: 'myfile.html'
# 常用操作
# path.basename(path[, ext])
返回一个 path
的最后一部分
path.basename('/foo/bar/baz/asdf/quux.html');
// 返回: 'quux.html'
path.basename('/foo/bar/baz/asdf/quux.html', '.html');
// 返回: 'quux'
# path.dirname(path)
返回一个 path
的目录名
path.dirname('/foo/bar/baz/asdf/quux');
// 返回: '/foo/bar/baz/asdf'
# path.join([...paths])
使用平台特定的分隔符把全部给定的 path
片段连接到一起,并规范化生成的路径。
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
// 返回: '/foo/bar/baz/asdf'
path.join('foo', {}, 'bar');
// 抛出 'TypeError: Path must be a string. Received {}'
# path.parse(path)
返回一个对象,对象的属性表示 path
的元素。
返回的对象有以下属性:
- dir
- root
- base
- name
- ext
path.parse('/home/user/dir/file.txt');
// 返回:
// { root: '/',
// dir: '/home/user/dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file' }
# URL 模块
url 模块提供了一些实用函数,用于 URL 处理与解析。
URL 字符串是具有结构的字符串,包含多个意义不同的组成部分。URL 字符串可以被解析为一个 URL 对象,其属性对应于字符串的各组成部分。
url 模块提供了两套 API 来处理 URL 字符串:
- 一个是 Node.js 特有的 API,是旧版本的遗留;
- 另一个则是实现了 WHATWG URL Standard 的 API ,该标准也在各种浏览器中被使用。
新的应用程序应当使用 WHATWG API。
下图中,网址 'http://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash'
上方是由遗留的 url.parse()
返回的对象的属性。下方的则是WHATWG URL
对象的属性。
利用WHATWG API解析一个URL字符串:
const { URL } = require('url');
const myURL = new URL('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
通过Node.js提供的API解析一个URL:
const url = require('url');
const myURL = url.parse('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
# WHATWG URL
根据 WHATWG URL 标准, 实现的 URL 类. 解析出来的 URL 对象的所有属性都是在类的原型上实现为 getter 和 setter,而不是作为对象本身的数据属性。因此,在 URL 对象的任何属性上使用 delete 关键字没有任何效果,但仍返回 true。
# new URL(input[, base])
input <string>
: 要解析的输入 URLbase <string> | <URL>
: 如果input
是相对 URL,则为要解析的基本 URL。
const { URL } = require('url');
const myURL = new URL('/foo', 'https://example.org/');
// https://example.org/foo
Unicode 字符将被使用 Punycode 算法自动转换为 ASCII:
const { URL } = require('url');
const myURL = new URL('https://你好你好');
// https://xn--6qqa088eba/
# url.toString()
在 URL 对象上调用 toString()
方法将返回序列化的 URL。返回值与 url.href
和 url.toJSON()
的相同。
# Legacy URL
旧版本的 URL 对象属性可以用 delete.
# url.format(urlObject)
url.format()
方法返回一个从 urlObject
格式化后的 URL 字符串。
# url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
url.parse()
方法会解析一个 URL 字符串并返回一个 URL 对象。
urlString <string>
: 要解析的 URL 字符串。parseQueryString <boolean>
如果为true
,则query
属性总会通过 querystring 模块的parse()
: 方法生成一个对象。 如果为false
,则返回的 URL 对象上的query
属性会是一个未解析、未解码的字符串。 默认为false
。slashesDenoteHost <boolean>
: 如果为true
,则//
之后至下一个/
之前的字符串会被解析作为host
。 例如,//foo/bar
会被解析为{host: 'foo', pathname: '/bar'}
而不是{pathname: '//foo/bar'}
。 默认为false
。
# Net 模块
提供了创建基于 stream流 的 TCP 或 IPC 服务器 和 客户端 的异步网络 API。
# 开启 TCP 或 IPC 服务
先看一个使用实例:
// server.js
var net = require('net');
var server = net.createServer(function(socket) {
console.log('server connected');
socket.on('end', function() {
console.log('server disconnected');
});
socket.on('data', function(){
socket.end('hello\r\n');
});
});
server.listen(3000, function() {
console.log('监听 3000 端口');
});
# net.Server 实例事件
NET 模块是继承 EventEmitter 的, 绑定了很多事件.
# 'close' 事件
当 server 关闭的时候触发, 所有的连接结束才会触发这个事件
# 'connection' 事件
当一个新的 connection 建立的时候触发, 返回 <net.Socket>
实例对象
# 'error' 事件
当错误出现的时候触发, 'close' 事件不会在这个事件触发后继续触发 除非是手动调用
# 'listening' 事件
当服务被绑定后调用 server.listen()
.
# 常用操作
# net.createServer([options][, connectionListener])
用于创建一个 TCP 或 IPC 服务, 返回 <net.Server>
对象.
options <Object>
(以后再说)allowHalfOpen <boolean>
: 表示是否允许一个半开的TCP连接。 默认值:false
pauseOnConnect <boolean>
: 一旦来了连接,是否暂停套接字。 默认值:false
connectionListener <Function>
: 为'connection' 事件自动设置一个监听器。
# server.listen([port[, host[, backlog]]][, callback])
port <number>
端口host <string>
主机backlog <number> server.listen()
最大连接数callback <Function> server.listen()
监听函数Returns: <net.Server>
为 'connections' 事件 启动一个 server 监听. 一个 net.Server
可以是一个 TCP 或者 一个 IPC server,这取决于它监听什么。
# 创建 Socket 与 Server 通信
net.Socket
可以被用户创建并直接与 server 通信。
当一个连接被接收时,它也能被 Node.js 创建并传递给用户。通过监听在一个 net.Server
上的'connection' 事件触发而获得的,用户可以使用它来与客户端通信。
// client.js
var net = require("net");
var client = net.connect({port: 3000}, function(){
console.log('client connected');
client.write('world!\r\n');
});
client.on('data', function(data) {
console.log(data.toString());
client.end();
});
client.on('end', function() {
console.log('client disconnected');
});
# net.Socket 实例事件
# 'close' 事件
一旦 socket 完全关闭就发出该事件。
# 'connect' 事件
当一个 socket 连接成功建立的时候触发该事件。
# 'data' 事件
当接收到数据的时触发该事件。data 参数是一个 Buffer 或 String。数据编码由 socket.setEncoding()
设置。
# 'drain' 事件
当写入缓冲区变为空时触发。可以用来做上传节流。
# 'end' 事件
当 socket 的另一端发送一个 FIN 包的时候触发,从而结束 socket 的可读端。
# 'error' 事件
当错误发生时触发。'close' 事件也会紧接着该事件被触发。
# 'lookup' 事件
在找到主机之后创建连接之前触发。
# 常用操作
# socket.connect(options[, connectListener])
在给定的套接字上启动一个连接。
IP 地址加上主机上的端口号作为 TCP 连接的端点,这种端点就叫做套接字(socket)或插口。
该方法是异步的。当连接建立了的时候,'connect' 事件将会被触发。如果连接过程中有问题,'error' 事件将会代替 'connect' 事件被触发,并将错误信息传递给 'error' 监听器。
返回 <net.Socket>
socket 自身。
常用 options 有:(其余的看文档吧)
port <number>
必须。Socket 连接的端口。host <string>
Socket 连接的主机。默认是 'localhost'
# net.createConnection(options[, connectListener])
一个用于创建 net.Socket 的工厂函数,立即使用 socket.connect()
初始化链接,然后返回启动连接的 net.Socket。
当连接建立之后,在返回的 socket 上将触发一个 'connect' 事件。
net.connect()
是 net.createConnection()
的别名
# socket.write(data[, encoding][, callback])
在 socket 上发送数据。第二个参数制定了字符串的编码 - 默认是 UTF8 编码。
如果全部数据都成功刷新到内核的缓冲则返回 true。如果全部或部分数据在排队,则返回 false。
当缓冲再次空闲的时候将触发 'drain' 事件。
# socket.end([data][, encoding])
半关闭 socket。例如发送一个 FIN 包。服务端仍可以发送数据。
Returns: <net.Socket>
Socket 本身。
如果指定了 data,则相当于调用 socket.write(data, encoding)
之后再调用 socket.end()
。
# HTTP 模块
在 Node 标准库提供了 http 模块, 可以让 HTTP 协议 应用起来更简易.
模块中封装了一个高效的 HTTP 服务器对象 和 一个简易的 HTTP 客户端.
http.Server
是一个基于事件的 HTTP 服务器.
http.request
则是一个 HTTP 客户端工具,用于向 HTTP 服务器发起请求.
# http.IncomingMessage
是 HTTP 请求的信息. 由 http.Server 或 http.ClientRequest 创建,作为 request 或 response 事件的第一个参数传递. 通常简称 request 或 req.
# 常用属性
message.headers
: 请求头或响应头的对象。message.method
: (仅在 http.Server 返回的请求中有效。) 返回一个字符串,表示请求的方法。 该属性只读。 例如:'GET'、'DELETE'。message.url
: (仅在 http.Server 返回的请求中有效。) 返回请求的 URL 字符串
# http.ServerResponse
http.ServerResponse 是返回给客户端的信息,在 HTTP 服务器内部 (http.Server) 被创建, 决定了用户最终能看到的结果。作为 request 或 response 事件的第二个参数传递. 通常简称 response 或 res.
# response.writeHead(statusCode[, statusMessage][, headers])
发送一个响应头给请求。 状态码是一个三位数的 HTTP 状态码,如 404。 最后一个参数 headers 是响应头。 第二个参数 statusMessage 是可选的状态描述。
该方法在消息中只能被调用一次
# response.write(chunk[, encoding][, callback])
方法会发送一块响应主体。 它可被多次调用,以便提供连续的响应主体片段。
chunk 可以是一个字符串或一个 buffer。 如果 chunk 是一个字符串,则第二个参数指定如何将它编码成一个字节流。
encoding 默认为 'utf8'。 当数据块被刷新时,callback 会被调用。
# response.end([data][, encoding][, callback])
该方法会通知服务器,所有响应头和响应主体都已被发送,即服务器将其视为已完成。 每次响应都必须调用 response.end()
方法。
如果指定了 data,则相当于调用 response.write(data, encoding)
之后再调用 response.end(callback)
。
如果指定了 callback,则当响应流结束时被调用。
# HTTP 服务器
http.Server 是 http 模块中的 HTTP 服务器对象. 用 Node.js 做的所有基于 HTTP 协议的系统都是基于 http.Server 实现的.
它提供了 一套封装级别很低的 API,仅仅是流控制和简单的消息解析,所有的高层功能都要通过它们接口来实现。
# http.createServer([options][, requestListener])
http.createServer
方法创建一个 http.Server 实例. 这个函数接受一个 HTTP 请求处理函数 (requestListner) 作为参数, 返回一个 http.Server 实例. requestListener 作为 'request' 事件监听函数。
// 引入 http 模块
var http = require('http');
// 创建实例
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write('I am Garrik');
res.end();
// 在 3000 端口监听
}).listen(3000);
在上面的代码中, 按照 HTTP 协议编写了 响应头部. 然后编写了 响应体. 然后通过 res.end
结束并发送. 调用 listen
函数去监听服务器的 3000 端口. 当有客户端访问 3000 端口, 触发 request 事件, 监听函数被调用, res 作为响应, 传递回客户端.
# Server 常用事件
首先 http.Server 是一个基于事件的 HTTP 服务器. 所有的请求都被封装为独立的事件, 开发者只需要对它的事件编写响应函数即可实现 HTTP 服务器的所有功能。
事件都继承自 EventEmitter,常用的几个有:
request
: 当收到客户端发来的请求时, 该事件触发. 发送两个参数 req 和 res, ,分别是http.IncomingMessage
和http.ServerResponse
的实例,表示请求和响应信息。connection
: 当 TCP 连接建立时,该事件被触发,提供一个参数 socket,为net.Socket
的实例。 connection 事件的粒度要大于 request,因为客户端在 Keep-Alive 模式下可能会在同一个连接内发送多次请求。(粒度大意思就是事件被触发频率高, 个人理解.)close
: 当服务器关闭时,该事件被触发。注意不是在用户连接断开时。
# HTTP 客户端
下回再说