美文网首页全栈开发
Node.js教程(01)|基础知识篇(上)

Node.js教程(01)|基础知识篇(上)

作者: 夏海峰 | 来源:发表于2019-07-17 18:21 被阅读26次
    关于 Node.js

    Node.js 就是运行在服务端的 JavaScript。Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

    • 安装后,node -v进行版本检测。
    • 执行node xxx.js,运行xxx.js脚本。
    • Node.js支持REPL交互式模式,执行node命令后进入交互模式。
    • Node.js使用require进行模块的引入。
    • Node.js可以创建类似Apache``Nginx一样的HTTP服务器,用于处理客户端的请求和响应。
    搭建第一个Node.js HTTP服务器
    #server.js
    
    var http = require('http');
    
    http.createServer(function(req, res) {
        // 设置HTTP状态值为200,内容类型为text/plain
        res.writeHead(200, {'Content-Type': 'text/plain'});
        // 发送响应数据
        res.end('hello world');
    }).listen(8888);
    
    console.log('Server running at http://172.0.0.1:8888/');
    

    使用require加载引入http模块,使用该模块的createServer函数创建HTTP服务器,listen方法设置监听端口。在命令中输入node server.js启动这个node.js HTTP服务器。并在浏览器和命令行中进行测试访问,如下图示:

    浏览器访问 curl 访问
    Node.js REPL交互模式

    REPL交互模式下,可以进行文件的读取、语句的执行、console打印等。演示如下:

    REPL交互模式
    Node.js 异步编程之回调函数

    Node.js 异步编程的直接体现就是回调函数。异步编程依托于回调来实现,但不能说使用了回调函数后程序就异步化了。回调函数是在完成任务后被调用的,Node 使用了大量的回调函数,Node.js中所有的 API 都支持回调函数。回调函数一般作为函数的最后一个参数出现。

    function foo1 (name, age, callback) { }
    function foo2 (value, callback1, callback2) { }
    

    例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数进行返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。

    阻塞(同步编程):阻塞是按顺序执行的。

    // sync.js  测试 阻塞
    
    var fs = require('fs');
    var data = fs.readFileSync('input.txt');
    console.log(data.toString());
    console.log('程序执行结束!');
    

    非阻塞(异步编程):非阻塞不需要按顺序执行,如果要处理异步执行的结果,在回调函数里去实现。

    // async.js  测试 非阻塞
    
    var fs = require('fs');
    fs.readFile('input.txt', function(err, data) {
        if (err) return console.error(err);
        if (data) {
            console.log(data.toString());
        }
    });
    console.log('程序执行结束!');
    
    Node.js 应用程序是如何工作的?
    var fs = require('fs');
    
    fs.readFile('input.txt', function(err, data) {
        if (err) {
            console.log(err.stack);
            return;
        }
        console.log(data.toString());
    });
    console.log('程序执行完毕');
    

    在Node.js 程序中,异步函数执行时,接收一个回调函数作为最后一个参数,该回调参数的第一个参数是err错误对象。当这个异步操作执行错误时,就会生成err错误对象;当没有错误发生时,就会跳过err对象的输出。

    Node.js 事件循环模型

    Node.js 是单进程单线程的应用程序,但基于 V8 引擎提供的异步执行回调接口,可以处理大量的并发操作,所以其性能非常高。每个异步事件都会生成一个事件观察者,如果有事件发生就会调用该回调函数。Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出。Node.js 所有的事件机制,基本上都采用了设计模式中观察者模式。

    事件驱动模型

    Node.js 使用事件驱动模型,当web server接收到请求后,立即关闭它然后进行处理,接着去服务下一个web请求。当这个请求完成后,它会被放回处理队列,当到达队列的开头时,这个处理结果将被返回给用户。

    这个模型非常高效、可扩展性非常强,因为web server只是接受请求,而不等待任何的读写操作,这被称之为非阻塞式IO或者事件驱动IO

    在事件驱动模型中,会生成一个主循环来监听所有的事件,当检测到事件时将触发回调函数。

    事件驱动模型

    整个事件驱动的流程就是这么实现的,非常简洁。这类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数都相当于是观察者(Observer)。

    Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定并监听事件,如下实例:

    var events = require('events');
    // 创建EventEmitter事件模型对象
    var ee = new events.EventEmitter();
    
    // 监听connection事件
    ee.on('connection', function() {
        console.log('1-连接成功');
        ee.emit('data_received');
    });
    
    // 监听data_received事件
    ee.on('data_received', function() {
        console.log('2-数据接收成功');
    });
    
    // 触发connection事件
    ee.emit('connection');
    console.log('3-程序执行完毕');
    

    events.EventEmitter类

    在Node.js中,所有的异步I/O操作在完成时都会发送一个事件到事件队列。events模块只提供了一个对象,即events.EventEmitter类,它的核心就是事件监听功能与事件触发功能的封装。同一个EventEmitter实例,可以同时监听多个同名事件:

    var events = require('events');
    
    var ee = new events.EventEmitter();
    
    ee.on('some', function(arg1, arg2) {
        console.log(arg1, arg2);
    });
    
    ee.on('some', function(arg1, arg2) {
        console.log(arg1, arg2);
    });
    
    ee.on('some', function(arg1, arg2) {
        console.log(arg1, arg2);
    });
    
    ee.emit('some', '参数1', '参数2');
    

    大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。

    Buffer 缓冲区

    JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。Buffer 库为 Node.js 带来了一种存储原始数据的方法。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存

    // 创建Buffer实例
    const buf = Buffer.from('geekxia', 'ascii');
    
    console.log(buf);
    console.log(buf.toString('hex'));
    console.log(buf.toString('base64'));
    
    // 写入缓冲区
    buf.write()
    // 从缓冲区中读取数据
    buf.toString()
    // 把Buffer转换为JSON对象
    buf.toJSON()
    // 合并缓冲区
    Buffer.concat([buf1, buf2]);
    // 缓冲区比较
    buf.compare(buf1);
    // 缓冲区拷贝
    buf.copy()
    // 缓冲区裁剪
    buf.slice()
    // 读取缓冲区的长度
    buf.length;
    
    Stream 流

    Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。所有的 Stream 对象都是 EventEmitter 的实例,常用的事件有:

    • data - 当有数据可读时触发。
    • end - 没有更多的数据可读时触发。
    • error - 在接收和写入过程中发生错误时触发。
    • finish - 所有数据已被写入到底层系统时触发。

    可读流、可写流

    使用fs.createReadStream()创建可读流,使用fs.createWriteStream()还可以创建可写流

    var fs = require('fs');
    var data = '';
    
    // 创建可读流
    var rs = fs.createReadStream('input.txt');
    rs.setEncoding('UTF8');
    
    // 处理流事件 - data, end, error
    rs.on('data', function(chunk) {
        data += chunk;
    });
    rs.on('end', function() {
        console.log(data);
    });
    rs.on('error', function(err) {
        console.log(err.stack);
    });
    
    console.log('程序执行完毕');
    

    管道流

    管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。

    管道流
    var fs = require('fs');
    
    // 创建一个可读流
    var rs = fs.createReadStream('input.txt');
    // 创建一个可写流
    var ws = fs.createWriteStream('output.txt');
    
    // 管道读写操作:读取input.txt中的内容,并将其写入到output.txt中去
    rs.pipe(ws);
    
    console.log('程序执行完毕');
    

    链式流

    链式流是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。接下来我们就是用管道和链式来实现文件的压缩和解压

    实现压缩:compress.js
    
    var fs = require('fs');
    var zlib = require('zlib');
    
    // 压缩input.txt 为 input.txt.gz
    fs.createReadStream('input.txt')
        .pipe(zlib.createGzip())
        .pipe(fs.createWriteStream('input.txt.gz'));
    
    console.log('文件压缩完成');
    
    实现解压:decompress.js
    
    var fs = require('fs');
    var zlib = require('zlib');
    
    // 把 input.txt.gz 解压为 input2.txt
    fs.createReadStream('input.txt.gz')
        .pipe(zlib.createGunzip())
        .pipe(fs.createWriteStream('input2.txt'));
    
    console.log('文件解压完成');
    


    Node.js 完整教程 第01篇,结束!

    相关文章

      网友评论

        本文标题:Node.js教程(01)|基础知识篇(上)

        本文链接:https://www.haomeiwen.com/subject/kqbphctx.html