theme: condensed-night-purple
highlight: a11y-dark
这是我参与「第四届青训营 」笔记创作活动的的第15天
https://www.runoob.com/nodejs/nodejs-tutorial.html
Node.js 应用是由哪几部分组成的:
引入 required 模块:我们可以使用 require 指令来载入 Node.js 模块。
创建服务器:服务器可以监听客户端的请求,类似于 Apache 、Nginx 等 HTTP 服务器。
接收请求与响应请求 服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。
```js // 使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http: var http = require("http");
// 创建服务器 // 使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口。 // 函数通过 request, response 参数来接收和响应数据
http.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888);
// 终端打印如下信息 console.log('Server running at http://127.0.0.1:8888/'); ```
分析Node.js 的 HTTP 服务器:
第一行请求(require)Node.js 自带的 http 模块,并且把它赋值给 http 变量。
接下来我们调用 http 模块提供的函数: createServer 。这个函数会返回 一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号。
Node.js 事件循环
- Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件
```js // 引入 events 模块 var events = require('events'); // 创建 eventEmitter 对象 var eventEmitter = new events.EventEmitter();
// 绑定事件及事件的处理程序 eventEmitter.on('eventName', eventHandler);
// 触发事件 eventEmitter.emit('eventName');
// 实例
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
// 创建事件处理程序
var connectHandler = function connected() {
console.log('连接成功。');
// 触发 data_received 事件
eventEmitter.emit('data_received');
}
// 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler);
// 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function () {
console.log('数据接收成功。');
});
// 触发 connection 事件
eventEmitter.emit('connection');
console.log("程序执行完毕。");
```
Node.js EventEmitter
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
```js // 引入 events 模块 var events = require('events'); // 创建 eventEmitter 对象 var eventEmitter = new events.EventEmitter();
//event.js 文件
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
console.log('some_event 事件触发');
});
setTimeout(function() {
event.emit('some_event');
}, 1000);
```
对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
EventEmitter 的属性
方法
addListener(event, listener) 为指定事件添加一个监听器到监听器数组的尾部
on(event, listener) 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数
js server.on('connection', function (stream) { console.log('someone connected!'); });
once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器
js server.once('connection', function (stream) { console.log('Ah, we have our first user!'); });
removeListener(event, listener)
移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。
它接受两个参数,第一个是事件名称,第二个是回调函数名称。
js var callback = function(stream) { console.log('someone connected!'); }; server.on('connection', callback); // ... server.removeListener('connection', callback);
removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器
setMaxListeners(n) 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于改变监听器的默认限制的数量。
listeners(event) 返回指定事件的监听器数组
emit(event, [arg1], [arg2], [...]) 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false
events.emitter.listenerCount(eventName)
/eventEmitter.listeners('connection').length
返回指定事件的监听器数量
```js // 实例 var events = require('events'); var eventEmitter = new events.EventEmitter();
// 监听器 #1
var listener1 = function listener1() {
console.log('监听器 listener1 执行。');
}
// 监听器 #2
var listener2 = function listener2() {
console.log('监听器 listener2 执行。');
}
// 绑定 connection 事件,处理函数为 listener1
eventEmitter.addListener('connection', listener1);
// 绑定 connection 事件,处理函数为 listener2
eventEmitter.on('connection', listener2);
var eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 个监听器监听连接事件。");
// 处理 connection 事件
eventEmitter.emit('connection');
// 移除监绑定的 listener1 函数
eventEmitter.removeListener('connection', listener1);
console.log("listener1 不再受监听。");
// 触发连接事件
eventEmitter.emit('connection');
eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 个监听器监听连接事件。");
console.log("程序执行完毕。");
// 以上代码,执行结果如下所示:
// $ node main.js
// 2 个监听器监听连接事件。
// 监听器 listener1 执行。
// 监听器 listener2 执行。
// listener1 不再受监听。
// 监听器 listener2 执行。
// 1 个监听器监听连接事件。
// 程序执行完毕。
``` https://www.runoob.com/nodejs/nodejs-event.html
# Node.js Buffer(缓冲区)
https://www.runoob.com/nodejs/nodejs-buffer.html
Node.js Stream(流)
Stream 是一个抽象接口
Node.js,Stream 有四种流类型:
Readable - 可读操作。
Writable - 可写操作。
Duplex - 可读可写操作.
Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
data - 当有数据 可读 时触发。
end - 没有 更多的数据可读时触发。
error - 在接收和写入过程中 发生错误 时触发
finish - 所有数据 已被写入 到底层系统时触发。
教程 : https://www.runoob.com/nodejs/nodejs-stream.html
```js // 从流中读取数据 var fs = require("fs"); var data = '';
// 创建可读流
var readerStream = fs.createReadStream('input.txt');
// 设置编码为 utf8。
readerStream.setEncoding('UTF8');
// 处理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
data += chunk;
});
readerStream.on('end',function(){
console.log(data);
});
// 写入流 var fs = require("fs"); var data = '菜鸟教程官网地址:www.runoob.com';
// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');
// 标记文件末尾
writerStream.end();
// 处理流事件 --> finish、error
writerStream.on('finish', function() {
console.log("写入完成。");
});
writerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
```
管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中
用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程
```js // 通过读取一个文件内容并将内容写入到另外一个文件中 var fs = require("fs");
// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');
// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);
console.log("程序执行完毕");
```
链式流
链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。
```js // 用管道和链式来压缩和解压文件
const fs = require("fs");
const zlib = require('zlib');
// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));
console.log("文件压缩完成。");
// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('input.txt'));
console.log("文件解压完成。");
```
Node.js模块系统
模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展
Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象
引入模块
js var hello = require('./hello'); // 代码 require('./hello') 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)
详情见模块化开发
服务端的模块放在哪里
详见 : https://www.runoob.com/nodejs/nodejs-module-system.html
Node.js 的 require 方法中的文件查找策略如下
从文件模块缓存中加载
尽管原生模块与文件模块的优先级不同,但是都会优先从文件模块的缓存中加载已经存在的模块。
从原生模块加载
原生模块的优先级仅次于文件模块缓存的优先级。require 方法在解析文件名之后,优先检查模块是否在原生模块列表中。以http模块为例,尽管在目录下存在一个 http/http.js/http.node/http.json 文件,require("http") 都不会从这些文件中加载,而是从原生模块中加载。
原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。
从文件加载
当文件模块缓存中不存在,而且不是原生模块的时候,Node.js 会解析 require 方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。
require方法接受以下几种参数的传递:
http、fs、path等,原生模块。 ./mod或../mod,相对路径的文件模块。 /pathtomodule/mod,绝对路径的文件模块。 mod,非原生模块的文件模块。
exports 和 module.exports 的使用 如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports
Node.js 函数
一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数(匿名函数)
Node.js 路由
我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码
详见 : https://www.runoob.com/nodejs/nodejs-router.html
```js // router.js 文件代码: function route(pathname) { console.log("About to route a request for " + pathname); }
exports.route = route;
// server.js 文件代码: var http = require("http"); var url = require("url");
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(pathname);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
// index.js 文件代码: var server = require("./server"); var router = require("./router");
server.start(router.route);
// out : Server has started. ```
Node.js 全局对象
Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性
它及其所有属性都可以在程序的任何地方访问,即全局变量
全局对象与全局变量
global 最根本的作用是作为全局变量的宿主
- 满足以下条 件的变量是全局变量: 在最外层定义的变量; 全局对象的属性; 隐式定义的变量(未定义直接赋值的变量)
当你定义一个全局变量时,这个变量同时也会成为全局对象的属性
注意: 最好不要使用 var 定义变量以避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险
__filename
表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径
__dirname
__dirname 表示当前执行脚本所在的目录
setTimeout(cb, ms)
setTimeout(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。:setTimeout() 只执行 一次 指定函数。
返回一个代表定时器的句柄值。
clearTimeout(t)
clearTimeout( t ) 全局函数用于 停止 一个之前通过 setTimeout() 创建的定时器。 参数 t 是通过 setTimeout() 函数创建的定时器。
```js function printHello(){ console.log( "Hello, World!"); } // 两秒后执行以上函数 var t = setTimeout(printHello, 2000);
// 清除定时器
clearTimeout(t);
```
setInterval(cb, ms)
setInterval(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。
返回一个代表定时器的句柄值。可以使用 clearInterval(t) 函数来清除定时器。
setInterval() 方法会 不停地 调用函数,直到 clearInterval() 被调用 或窗口被 关闭
js function printHello(){ console.log( "Hello, World!"); } // 两秒后执行以上函数 setInterval(printHello, 2000); // 以上程序每隔两秒就会输出一次"Hello, World!",且会永久执行下去,直到你按下 ctrl + c 按钮。
console
console 用于提供控制台标准输出, 用于向标准输出流(stdout)或标准错误流(stderr)输出字符
- console 方法
console.log([data][, ...])
: 向标准输出流打印字符并以 换行符结束。 该方法接收若干 个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。如果没有 参数,只打印一个换行js console.log('Hello world'); console.log('byvoid%diovyb'); console.log('byvoid%diovyb', 1991); // Hello world // byvoid%diovyb // byvoid1991iovyb
- console.time(label) : 输出时间,表示计时开始。
- console.timeEnd(label) : 结束时间,表示计时结束。
- console.error([data][, ...]) : 输出错误消息的。控制台在出现错误时会显示是红色的叉子。
- console.warn([data][, ...]) : 输出警告消息。控制台出现有黄色的惊叹号。
详见 :https://www.runoob.com/nodejs/nodejs-global-object.html
process
process 是一个全局变量,即 global 对象的属性。
它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。通常在你写本地命令行程序的时候,少不了要 和它打交道。下面将会介绍 process 对象的一些最常用的成员方法。
详见 :https://www.runoob.com/nodejs/nodejs-global-object.html