Node.js快速入门

作者: 凛_冬_将_至 | 来源:发表于2017-04-18 21:35 被阅读235次

    一、概述

    Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

    Node.js是一个基于Google Chrome的V8引擎的JavaScript运行环境,它允许使用JavaScript语言编写服务器端代码。Node.js具有事件驱动和I/O非阻塞两大特点,轻量而高效。

    官网地址

    二、安装与运行

    从官网下载最新版本的Node.js,直接安装。安装时选择全部组件,并添加到环境变量Path中。

    运行命令:node -v查看是否安装成功。

    运行命令:node进入到node.js的交互环境,可以输入任意JavaScript语句,并查看运行结果。

    运行命令:node <*.js>直接执行js文件里的内容。

    运行命令:node --use_strict <*.js>为js文件开启严格模式。

    安装Node.js的时候,会自动安装包管理工具npm。

    运行命令:npm -v查看npm的版本。

    命令行模式会一次性执行js文件,中间没有交互;交互模式则是每一行单独执行,可以进行交互。

    如果不想使用集成的IDE,可以使用VS Code编辑器进行Node.js的开发和调试。具体操作和配置可以参照VS Code的官网教程。

    VS Code官网地址

    Node.js Applications with VS Code

    三、模块

    在Node.js环境中,一个js文件就称之为一个模块。使用模块提高了代码的可维护性以及可重用性,还避免了函数名和变量名冲突。

    当在模块中定义一个对象(对象、函数、数组等)时,可以输出这个对象以供其他模块使用:

    module.exports = object_name;
    

    其他模块要想使用这个对象,可以在模块中引入该对象:

    var variable_name = require('/path/to/the/module/module_name');
    

    Tips:如果不指定路径,Node会按照内置模块、全局模块、当前模块的顺序进行查找。

    1、基本模块

    在浏览器中,JavaScript有一个全局对象window;而在Node.js环境中,也有一个全局对象global。由于JavaScript代码既能在浏览器执行,也能在Node环境下执行,可以通过全局对象名称来判断JavaScript代码是在哪个环境下执行的。

    process也是Node.js提供的一个对象,表示当前Node.js进程,进程本身的事件由process来处理。

    2、常用内置模块

    fs模块是文件系统模块,负责文件的读写操作,提供了同步和异步两种方法。大部分作为服务端的代码,必须使用异步代码。但是在服务器启动时读取配置文件,或结束时写入状态等操作,因为这些代码只执行一次,所以可以使用同步方法。

    读文本文件:

    var fs = require('fs');
    fs.readFile('input.txt', 'utf-8', function (err, data) {
      if (err) {
        console.log(err);
      } else {
        console.log(data);
      }
    });
    

    其中,readFile函数的第一个参数是文件名,第二个参数是文件编码,第三个参数是一个回调函数。回调函数的第一个参数代表错误信息,第二个参数代表返回结果。

    读二进制文件:

    var fs = require('fs');
    fs.readFile('input.png', function (err, data) {
      if (err) {
        console.log(err);
      } else {
        console.log(data);
        console.log(data.length + ' bytes');
      }
    });
    

    其中,不需要文件编码,并且回调函数的data参数将返回一个Buffer对象。Buffer对象是一个包含零个或任意个字节的数组。

    写文件:

    var fs = require('fs');
    var data = 'Hello world';
    fs.writeFile('output.txt', data, function (err) {
      if (err) {
        console.log(err);
      } else {
        console.log('success');
      }
    });
    

    其中,writeFile函数的第一个参数是文件名,第二个参数是要写入的数据,第三个参数是回调函数。

    读取文件的相关信息:

    fs.stat('input.txt', function (err, stat) {
      ...
    });
    

    stream是一个只能在服务端使用的模块,用来支持流数据结构,流中的数据是有序的。流是一个对象,使用时只需关注流的事件即可。data事件表示流的数据可以读取,end事件表示流没有数据可以读取,error事件表示出现错误。

    读文件:

    var fs = require('fs');
    var rs = fs.createReadStream('input.txt', 'utf-8');
    rs.on('data', function (chunk) {
      console.log('DATA:');
      console.log(chunk);
    });
    rs.on('end', function () {
      console.log('END');
    });
    rs.on('error', function (err) {
      console.log('ERROR: ' + err);
    });
    

    写文件:

    var fs = require('fs');
    var ws1 = fs.createWriteStream('output1.txt', 'utf-8');
    ws1.write('使用Stream写入文本数据...\n');
    ws1.write('END.');
    ws1.end();
    
    var ws2 = fs.createWriteStream('output2.txt');
    ws2.write(new Buffer('使用Stream写入二进制数据...\n', 'utf-8'));
    ws2.write(new Buffer('END.', 'utf-8'));
    ws2.end();
    

    可以使用管道操作pipe将读取流和写入流串联起来:

    var fs = require('fs');
    var rs = fs.createReadStream('input.txt');
    var ws = fs.createWriteStream('output.txt');
    rs.pipe(ws);
    

    默认情况下,当读取流的数据读取完毕,会触发end事件,并自动关闭写入流。如果不想关闭,需要传递额外的参数。

    readable.pipe(writable, { end: false });
    

    http模块是web应用最常用的一个模块,提供了request和response对象。request对象封装HTTP请求,调用request对象的属性和方法,可以获取HTTP请求的所有信息;response对象封装HTTP响应,调用response对象的方法,可以把HTTP响应返回给浏览器。

    var http = require('http');
    var server = http.createServer(function (request, response) {
      // 获取HTTP请求的method和url
      console.log(request.method + ':' + request.url);
      // 将HTTP响应的内容写入response
      response.writeHead(200, {'Content-Type': 'text/html'});
      response.end('<h1>Hello world!</h1>');
    });
    // 监听8080端口
    server.listen(8080);
    

    url模块可以用来将URL字符串解析成一个Url对象,从而获取想要的信息。

    var url = require('url');
    console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash'));
    

    path模块可以用来处理本地文件目录。

    var path = require('path');
    // 解析当前目录
    var workDir = path.resolve('.');
    // 组合完整的文件路径
    var filePath = path.join(workDir, 'pub', 'index.html');
    

    crypto模块提供通用的加密和哈希算法。

    MD5哈希算法用来给任意数据一个签名,通常用一个十六进制字符串表示:

    const crypto = require('crypto');
    const hash = crypto.createHash('md5');
    hash.update('Hello, world!');
    console.log(hash.digest('hex'));
    

    Hmac哈希算法可以利用MD5等算法,另外还需要一个密钥:

    const crypto = require('crypto');
    const hmac = crypto.createHmac('sha256', 'secret-key');
    hmac.update('Hello, world!');
    console.log(hmac.digest('hex'));
    

    AES是一种常用的对称加密算法,加密与解密使用同一个密钥。crypto模块提供了AES支持,但需要自行封装。DiffieHellman算法是一种密钥交换协议,可以让双方自行协商一个密钥。crypto模块还可以处理数字证书。

    四、Web开发

    使用Node.js开发Web服务器端,有几点好处:

    • 前后端统一使用JavaScript,不需要切换语言
    • 异步处理大大提高了运行速度

    针对Node.js,出现了很多后端相关的Web框架、ORM框架、模板引擎、测试框架、构建工具等。

    1、Web框架Express/koa

    Express是第一代最流行的Web服务端框架,它对Node.js的http进行了封装。虽然Express的API非常简单,但是由于是基于ES5,要实现异步,只能使用回调。如果异步嵌套层次过多,代码很难理解。

    var express = require('express');
    var app = express();
    app.get('/', function (req, res) {
      res.send('Hello World!');
    });
    app.listen(3000, function () {
      console.log('Example app listening on port 3000!');
    });
    

    koa1基于ES6,使用generator实现异步。虽然使用generator在写法上比回调简单,但其本意并不是用于异步。真正设计用来实现异步的是Promise,但Promise的写法过于复杂。

    var koa = require('koa');
    var app = koa();
    app.use('/test', function *() {
      yield doReadFile1();
      var data = yield doReadFile2();
      this.body = data;
    });
    app.listen(3000);
    

    koa2基于ES7,使用Promise和关键字async配合实现异步,同时兼容generator的写法。

    app.use(async (ctx, next) => {
      await next();
      var data = await doReadFile();
      ctx.response.type = 'text/plain';
      ctx.response.body = data;
    });
    

    2、ORM框架Sequelize

    ORM技术是将关系型数据库的表结构映射到对象上,Sequelize返回Promise对象,可以更好地进行异步处理。

    ES6:

    Pet.findAll()
     .then(function (pets) {
       for (let pet in pets) {
         console.log(`${pet.id}: ${pet.name}`);
       }
     }).catch(function (err) {
       // error
     });
    

    ES7:

    (async () => {
      var pets = await Pet.findAll();
    })();
    

    要想使用Sequelize操作数据库,首先需要创建一个Sequelize实例。然后定义数据模型Model,使用Sequelize映射数据库表。这样就可以调用实例的相应方法来对数据库进行操作。

    Sequelize操作数据库的一般步骤:

    • 通过Model对象的findAll()方法获取实例
    • 如果要更新实例,先对实例属性进行赋值,然后调用save()方法
    • 如果要删除实例,直接调用destroy()方法

    3、模板引擎Jade/Pug

    Node.js默认使用Jade作为模板引擎(现改名为Pug)。Jade是Node.js的一个模块,jade文件可以被预编译为.js文件,也可以被编译为目标html代码。

    主页

    4、测试框架Mocha

    Mocha是JavaScript的单元测试框架,既可以在浏览器环境运行,也可以在Node.js的环境运行。

    特点:

    • 既可以测试简单的JavaScript函数,又可以测试异步代码
    • 既可以自动运行所有测试,也可以只运行特定的测试
    • 可以支持before、after、beforeEach和afterEach来编写初始化代码

    5、构建工具Webpack

    6、WebSocket协议

    WebSocket利用HTTP协议建立连接,在浏览器和服务器之间建立双向通信的通道,服务器可以主动向浏览器发送消息,而不需要通过浏览器发送请求。

    首先,WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求。

    GET ws://localhost:3000/ws/chat HTTP/1.1
    Host: localhost
    Upgrade: websocket
    Connection: Upgrade
    Origin: http://localhost:3000
    Sec-WebSocket-Key: client-random-string
    Sec-WebSocket-Version: 13
    

    与普通HTTP请求的区别:

    • GET请求的地址以ws://开头
    • 请求头Upgrade: websocketConnection: Upgrade表示连接将被转换为WebSocket连接
    • Sec-WebSocket-Key用于标识这个连接
    • Sec-WebSocket-Version指定WebSocket的协议版本

    然后,如果服务器接受请求,会返回响应:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: server-random-string
    

    其中,响应码101表示将切换协议,切换后的协议通过Upgrade来指定。

    在Node.js中,最常用的WebSocket模块是ws。创建一个WebSocket的服务器实例:

    const WebSocket = require('ws');
    const WebSocketServer = WebSocket.Server;
    const wss = new WebSocketServer({
      port: 3000
    });
    

    如果有WebSocket请求接入,wss对象可以响应connection事件来处理这个WebSocket。对于每个WebSocket连接,都要对它绑定某些事件方法来处理不同的事件。


    参考文章:

    廖雪峰:Node.js教程

    相关文章

      网友评论

        本文标题:Node.js快速入门

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