# 核心模块

核心模块是 Node.js 的心脏,它由一些精简而高效的库组成,为 Node.js 提供了基本的 API。

# Event 模块

Events 文档

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 文档

fs 模块是文件操作的封装,它提供了文件的读取、写入、更名、删除、遍历目录、链接等类似标准 POSIX 函数的方式与文件系统进行交互

fs 模块中所有的操作都提供了异步的和 同步的两个版本

异步形式的最后一个参数都是完成时回调函数。 传给回调函数的参数取决于具体方法,但回调函数的第一个参数都会保留给异常。 如果操作成功完成,则第一个参数会是 nullundefined

当使用同步操作时,任何异常都会被立即抛出,可以使用 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 模块提供了一些工具函数,用于处理文件与目录的路径。

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 对象,其属性对应于字符串的各组成部分。

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对象的属性。

Screen Shot 2018-07-15 at 5.11.20 PM

利用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> : 要解析的输入 URL
  • base <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.hrefurl.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 模块

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 模块

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.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.IncomingMessagehttp.ServerResponse 的实例,表示请求和响应信息。

  • connection: 当 TCP 连接建立时,该事件被触发,提供一个参数 socket,为 net.Socket 的实例。 connection 事件的粒度要大于 request,因为客户端在 Keep-Alive 模式下可能会在同一个连接内发送多次请求。(粒度大意思就是事件被触发频率高, 个人理解.)

  • close: 当服务器关闭时,该事件被触发。注意不是在用户连接断开时。

# HTTP 客户端

http.ClientRequest 文档

下回再说

# 参考

上次更新: 7/4/2020, 4:14:54 AM