一篇入门Node.js

1、Node.js 简介

简单来说,Node.js 是一个基于 Google V8 引擎的 JavaScript 环境,它支持在后端运行 JavaScript

它有三个显著的特点,分别是 单线程异步 I/O事件驱动

下面介绍在 Windows 下安装 Node.js 的方法:

首先打开 Node.js 的官方下载网站,https://nodejs.org/en/download/

  • 通过 Windows Installer 安装

选择 Windows Installer,根据电脑配置下载合适的 .msi 文件(32-bit / 64-bit),然后按照安装器的指引安装 Node.js 即可

按照这种方式安装 Node.js 会 自动配置全局环境,若安装成功,打开命令行,输入 node --version 命令可查看 Node.js 版本,输入 node 命令可进入 Node.js 命令行

  • 通过 Windows Binary 安装

选择 Windows Binary,根据电脑配置下载合适的 .zip 文件(32-bit / 64-bit),然后解压压缩文件到合适的文件夹即可

若安装成功,打开命令行,切换到安装目录下,输入 node --version 命令可查看 Node.js 版本,输入 node 命令可进入 Node.js 命令行

2、Node.js NPM

NPM 是与 Node.js 一起安装的 包管理工具,其具有以下作用:

  • 允许用户从 NPM 服务器下载别人编写的第三方包或命令行程序到本地使用
  • 允许用户将自己编写的第三方包或命令行程序上传到 NPM 服务器供别人使用

可以使用 npm -v 命令查看 npm 版本,可以使用 npm install npm -g 升级 npm 版本

(1)安装模块

在 npm 中,安装模块的方式有两种,分别是本地安装 (local) 和全局安装 (global)

本地安装 将模块放在当前目录中的 node_modules 目录 下,可以通过 require() 引入程序

全局安装 将模块放在 /usr/local 目录 下或 Node.js 的安装目录 下,可以直接在 命令行 中使用

如果希望具备两者功能,则需要同时在本地和全局安装模块

它们的语法格式如下:

npm install moduleName      # 本地安装
npm install moduleName -g   # 全局安装,参数 g 指定为 global

例如:

npm install express

(2)查看信息

我们可以使用 npm list 命令查看已安装的模块,例如:

npm list

如果需要查看指定的模块,可以在上述命令后面指定名称,例如:

npm list express

若向要了解更详细的信息,可以查看模块的 package.json 文件

package.json 文件位于模块的目录下,用于定义模块的属性,其中比较重要的属性如下:

  • name:包的名称
  • description:包的描述
  • version:包的版本号

npm 使用语义版本号来管理代码,分为 X.Y.Z 三位,分别代表主版本号、次版本号和补丁版本号

若修复漏洞,则更新 Z;若新增功能但向下兼容,则更新 Y;若有大变动且不向下兼容,则更新 X

  • author:包的作者姓名
  • contributors:包的贡献者姓名
  • license:包的许可证类型,默认为 ISC
  • repository:包代码存放地方的类型,可以是 git 或 svn
  • homepage:包的官网地址
  • keywords:关键字
  • dependencies:依赖包列表,如果依赖包没有安装,则会将它们安装在 node_module 目录
  • main:程序的主入口文件,require() 将加载这个文件,默认是根目录下的 index.js 文件
  • files:程序文件

(3)更新模块

可以使用 npm update moduleName 命令更新模块,例如:

npm update express

(4)搜索模块

可以使用 npm search moduleName 命令搜索模块,例如:

npm search express

(5)卸载模块

可以使用 npm uninstall moduleName 命令卸载模块,例如:

npm uninstall express

3、Node.js 模块

(1)原生模块

模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的

换言之,一个文件就是一个模块,这个文件可能是 JavaScript、JSON 或者编译过的 C/C++ 扩展

其中,原生模块是内置在 Node.js 中的模块,我们只需使用 require 将其引入到程序中即可使用

下面会详细介绍 Node.js 中常见的原生模块

(2)自定义模块

创建自定义模块也十分简单

比如,创建一个模块文件 greeting.js,把 属性和方法 封装到模块中,代码如下:

exports 定义模块的公开接口】

exports.hello = function() {
    console.log('Hello World');
}

然后,就可以在主程序文件 main.js 中引用 greeting 模块:

require 用于获取模块的接口,即所引用模块的 exports 对象】

var greeting = require('./greeting'); // 加载 greeting 模块,默认后缀为 .js
greeting.hello(); // 调用 exports 对象的成员函数

// Hello World

我们还可以把 对象 封装到模块中,例如,更改模块文件 greeting.js,代码如下:

function Hello() {
    this.hello = function() {
        console.log('Hello World');
    };
};
module.exports = Hello;

这样就可以直接在主程序文件 main.js 中获得这个对象,而不是原来的 exports 对象

var greeting = require('./greeting'); 
greet = new greeting(); 
greet.hello();

// Hello World

(3)模块系统

Node.js 中 require 方法的文件加载策略如下:

  • 文件模块缓存 中加载
  • 原生模块缓存 中加载
  • 原生模块 中加载
  • 文件 中加载(相对路径 / 绝对路径)

4、Node.js 事件

events 模块 只包含一个EventEmitter 类,用于 触发和监听事件

在 Node.js 中,有许多对象都会触发事件,而这些所有的对象都是 EventEmitter 类的实例

EventEmitter 具有以下常用的方法:

  • on(event, listener):为指定事件注册一个监听器 (回调函数),当事件触发时调用回调函数
  • once(event, listener):为指定事件注册一个单次监听器,单次监听器触发一次后立刻解除
  • addListener(event, listener):为指定事件添加一个监听器
  • removeListener(event, listener):为指定事件移除一个监听器
  • removeAllListeners([event]):为指定事件移除所有监听器,如果没有指定事件,那么移除所有事件的所有监听器
  • emit(event, [arg1], [arg2] ... [argn]):触发事件,执行事件相关的所有回调函数,并将参数传递给回调函数
  • setMaxListeners(n):设置监听器限制数量,默认为 10,添加监听器超过该数量会输出警告
  • listeners(event):返回指定事件的监听器数组
  • listenerCount(event):返回指定事件的监听器个数
// 引入 events 模块
var events = require('events');
// 实例化 events.EventEmitter 对象
var emitter = new events.EventEmitter();

// 为 someEvent 事件注册监听器
emitter.on('someEvent', function(arg) { 
    console.log('on: someEvent was triggered,', arg); 
}); 
// 为 someEvent 事件注册单次监听器
emitter.once('someEvent', function(arg) { 
    console.log('once: someEvent was triggered,', arg); 
});
// 定义监听器 listenner
var listener = function listener(arg) {
   console.log('addListener: listener is on,', arg);
}
// 为 someEvent 事件添加监听器 listenner
emitter.addListener('someEvent', listener);

// 触发事件 someEvent,并将参数传递给监听器
emitter.emit('someEvent', 'first');

// 为 someEvent 事件移除监听器 listenner
emitter.removeListener('someEvent', listener);
// 再次触发事件 someEvent,并将参数传递给监听器
emitter.emit('someEvent', 'second'); 

/* ------ 程序执行结果
    on: someEvent was triggered, first
    once: someEvent was triggered, first
    addListener: listener is on, first
    on: someEvent was triggered, second
------------- */

另外,当程序运行过程中出现错误时,会自动触发 error 事件

如果没有定义 error 事件的监听器,将会退出程序并输出错误信息

所以,为了避免程序遇到错误后崩溃,一般需要为 error 事件设置监听器

var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.emit('error'); // 主动触发 error 事件

/* ------ 程序执行结果
    event.js:173
        throw err; // Unhandled 'error' event
    ......
------------- */

最后需要知道,在大多数情况下我们不会直接使用 EventEmitter,而是在对象中继承它

在 Node.js 中,只要是支持事件响应的核心模块(包括 http、fs 等等)都是 EventEmitter 的子类

详细内容请看官方文档:https://nodejs.org/api/events.html

5、Node.js Buffer

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型,在某些情况下十分不方便

因此 Node.js 中包含一个 Buffer 类,用来创建一个专门存放二进制数据的缓存区

Buffer 实例一般用于表示 编码字符的序列,比如 ASCII、UTF-8、Base64 编码的数据

通过使用显式的字符编码,就可以在 Buffer 实例JavaScript 字符串 之间进行转换

目前,Node.js 支持的 字符编码 包括:

  • ascii:仅支持 7 位的 ASCII 字符
  • latin1:单字节编码,向下兼容ASCII,又名 binary
  • utf8:多字节编码的 Unicode 字符
  • utf16le:小字节序编码的 Unicode 字符,又名 ucs2
  • base64:Base64 编码
  • hex:将每个字节编码为两个十六进制字符

以下介绍常见的方法:

  • Buffer.alloc(size[, fill[, encoding]]):创建一个指定大小的 Buffer 实例
    • size:缓冲区大小
    • fill:指定初始化的值,默认初始化为 0
    • encoding:编码方式
  • Buffer.allocUnsafe(size):创建一个指定大小的 Buffer 实例,不会被初始化

    • size:缓冲区大小
  • Buffer.allocUnsafeSlow(size)

    • size:缓冲区大小
  • Buffer.from(array):从数组中创建一个 Buffer 实例

    • array:数组,用于初始化 Buffer,数组元素只能是数字,否则会自动被 0 覆盖
  • Buffer.from(string[, encoding]):从字符串中创建一个 Buffer 实例
    • string:字符串,用于初始化 Buffer
    • encoding:编码方式
  • Buffer.from(buffer):从缓存区创建一个 Buffer 实例

    • buffer:缓存区,用于初始化 Buffer
    // 创建一个长度为 10、初始化为 0x1 的 Buffer
    const buffer1 = Buffer.alloc(10, 1);
    
    // 创建一个长度为 10、未被初始化的 Buffer
    // 需要使用 fill() 或 write() 重写
    const buffer2 = Buffer.allocUnsafe(10);
    
    // 创建一个初始化为 [0x1, 0x2, 0x3] 的 Buffer
    const buffer3 = Buffer.from([1, 2, 3]);
    
    // 创建一个初始化为 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer
    const buffer4 = Buffer.from('tést');
    
    // 创建一个初始化为 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer
    const buffer5 = Buffer.from('tést', 'latin1');
  • buffer.write(string[, offset[, length]][, encoding]):写入 Buffer

    该方法返回实际写入的大小,若 Buffer 空间不足, 则只会写入部分字符串

    • string:写入缓冲区的字符串
    • offset:开始写入的索引位置,默认为 0
    • length:写入的字节数,默认为 buffer.length
    • encoding:使用的编码,默认为 'utf8'
    buffer = Buffer.alloc(256);
    len = buffer.write("Hello World");
    console.log(len);
    
    // 11
  • buffer.toString([encoding[, start[, end]]]):读取 Buffer,该方法返回指定编码的字符串
    • encoding:使用的编码,默认为 'utf8'
    • start:开始读取的索引位置,默认为 0
    • end:结束读取的索引位置,默认为 buffer.length
    const buffer = Buffer.from('Hello');
    console.log(buffer.toString('base64'));
    console.log(buffer.toString('hex'));
    
    // SGVsbG8=
    // 48656c6c6f
  • Buffer.concat(list[, totalLength]):合并缓冲区
    • list:用于合并的 Buffer 对象数组
    • totalLength:指定合并后 Buffer 对象的总长度
    var buffer1 = Buffer.from(('Hello '));
    var buffer2 = Buffer.from(('World'));
    var buffer3 = Buffer.concat([buffer1,buffer2]);
    console.log(buffer3.toString());
    
    // Hello World
  • buffer.compare(otherBuffer):比较缓冲区

    • otherBuffer:用于比较的 Buffer 对象
    var buffer1 = Buffer.from('ABC');
    var buffer2 = Buffer.from('ABCD');
    var result = buffer1.compare(buffer2);
    console.log(result);
    
    // -1
  • sourceBuffer.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]):拷贝缓冲区
    • targetBuffer:目标 Buffer 对象,将 sourceBuffer 拷贝到 targetBuffer 中
    • targetStart:开始拷贝的索引位置,默认为 0
    • sourceStart:源 Buffer 对象的开始位置,默认为 0
    • sourceEnd:源 Buffer 对象的结束位置,默认为 buffer.length
    var sourceBuffer = Buffer.from('abcdefg');
    var targetBuffer = Buffer.from('ABCDEFG');
    sourceBuffer.copy(targetBuffer, 3, 0, 3);
    console.log(sourceBuffer.toString());
    console.log(targetBuffer.toString());
    
    // abcdefg
    // ABCabcG
  • buffer.slice([start[, end]]):剪切缓冲区,该方法返回一个新的缓冲区,和旧缓冲区指向同一块内存
    • start:开始位置,默认为 0
    • end:结束位置,默认为 buffer.length
    var buffer1 = Buffer.from('Hello World');
    var buffer2 = buffer1.slice(6,11);
    console.log(buffer2.toString());
    buffer2.write('Node!')
    console.log(buffer1.toString());
    
    // World
    // Hello Node!
  • buffer.length:返回 Buffer 长度

    var buffer = Buffer.from('Hello');
    console.log(buffer.length);
    
    // 5

6、Node.js 文件系统

fs 模块提供类似 UNIX 标准的 文件操作 API,其中所有的方法均有 异步同步 版本

例如,读取文件就有同步方法(readFileSync)和异步方法(readFile)

在同步模式下,程序执行 I/O 的代码后会暂停执行,等待 I/O 返回结果,使得执行效率大大降低

// 引入 fs 模块
var fs = require("fs");
// 读取文件内容
// text.txt 文件中包含内容 Hello World
var data = fs.readFileSync('text.txt');
// 输出文件内容
console.log(data.toString());
// 控制台输出
console.log("Finish");

/* ------ 程序执行结果
    Hello World
    Finish
------------- */

在异步模式下,程序执行 I/O 的代码后会继续执行其它代码,等待 I/O 完成后,触发一个内部事件并将其加入到事件队列,然后通过执行这个事件的回调函数,处理 I/O 返回的结果

// 引入 fs 模块
var fs = require("fs");
// 读取文件内容
// text.txt 文件中包含内容 Hello World
fs.readFile('text.txt', function(err, data) {
    // 如果程序发生错误,则输出错误信息
    if (err) return console.error(err);
    // 如果程序正常运行,则输出文件内容
    console.log(data.toString());
});
// 控制台输出
console.log("Finish");

/* ------ 程序执行结果
    Finish
    Hello World
------------- */

由于异步方法性能更高,而且异步方法与同步方法的区别仅仅在于回调函数,所以下面只会介绍常用的异步方法:

  • open(path, flags[, mode], callback):打开文件

    • path:文件路径

    • flags:文件打开方式,具体值如下:

      描述
      r 以读取模式打开文件,如果文件不存在则抛出异常
      r+ 以读写模式打开文件,如果文件不存在则抛出异常
      rs 以同步的方式读取文件
      rs+ 以同步的方式读取和写入文件
      w 以写入模式打开文件,如果文件不存在则创建
      wx 类似于 w,但若文件路径存在,则文件写入失败
      w+ 以读写模式打开文件,如果文件不存在则创建
      wx+ 类似 w+,但若文件路径存在,则文件读写失败。
      a 以追加模式打开文件,如果文件不存在则创建
      ax 类似 a, 但若文件路径存在,则文件追加失败
      a+ 以读取追加模式打开文件,如果文件不存在则创建
      ax+ 类似 a+, 但若文件路径存在,则文件读取追加失败
    • mode:文件权限,文件创建默认权限为 0666(可读,可写)

    • callback:回调函数,有两个参数 err、fd,err 是错误对象,fd 是文件描述符

  • close(fd, callback):关闭文件

    • fd:文件描述符
    • callback:回调函数,没有参数
    var fs = require("fs");
    // 打开文件
    fs.open('file.txt', 'r+', function(err, fd) {
        if (err) {
          return console.error(err);
        }
        console.log("文件打开成功");
        // 关闭文件
        fs.close(fd, function(err){
            if (err){
              console.log(err);
            } 
            console.log("文件关闭成功");
        });
    });
  • stat(path, callback):获取文件信息

    • path:文件路径

    • callback:回调函数,有两个参数 err、stats,err 是错误对象,stats 是 fs.stats 对象

      stats 对象常用的方法如下:

      方法 描述
      isFile() 如果是文件返回 true,否则返回 false
      isDirectory() 如果是目录返回 true,否则返回 false
      isBlockDevice() 如果是块设备返回 true,否则返回 false
      isCharacterDevice() 如果是字符设备返回 true,否则返回 false
      isSymbolicLink() 如果是软链接返回 true,否则返回 false
      isFIFO() 如果是 FIFO 返回true,否则返回 false
      isSocket() 如果是 Socket 返回 true,否则返回 false
    var fs = require("fs");
    fs.stat('input.txt', function (err, stats) {
      if (err) {
            return console.error(err);
        }
        // 输出 stats 对象
        console.log(stats);
        // 检测文件类型
        console.log(stats.isFile());
        console.log(stats.isDirectory());
    });
  • writeFile(file, data[, options], callback):写入文件

    • file:文件名或文件描述符

    • data:要写入文件的数据,可以是 String 或 Buffer 对象

    • options:包含 {encoding, mode, flag} 的对象

      在默认情况下,encoding = utf8,mode = 0666,flag = 'w'

    • callback:回调函数,有一个参数 err

    var fs = require("fs");
    var data = 'Hello World';
    fs.writeFile('file.txt', data, function(err) {
        if (err) {
          return console.error(err);
        }
    });
  • read(fd, buffer, offset, length, position, callback):读取文件
    • fd:文件描述符
    • buffer:数据写入的缓冲区
    • offset:缓冲区的写入偏移量
    • length:文件读取的字节数
    • position:文件读取的起始位置,如果 position 为 null,则会从当前文件指针的位置读取
    • callback:回调函数,有三个参数 err、bytesRead、buffer),err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象
    var fs = require("fs");
    var buf = new Buffer.alloc(1024);
    fs.open('file.txt', 'r+', function(err, fd) {
        if (err) {
            return console.error(err);
        }
        fs.read(fd, buf, 0, buf.length, 0, function(err, bytes) {
            if (err) {
              console.log(err);
            }
            if (bytes > 0) {
              console.log(buf.slice(0, bytes).toString());
            }
        });
    });
  • ftruncate(fd, len, callback):截取文件
    • fd:文件描述符
    • len:截取长度
    • callback:回调函数,没有参数
    var fs = require("fs");
    var buf = new Buffer.alloc(1024);
    fs.open('file.txt', 'r+', function(err, fd) {
        if (err) {
          return console.error(err);
        }
        fs.ftruncate(fd, 10, function(err) {
            if (err){
              console.log(err);
            } 
        });
    });
    
  • unlink(path, callback):删除文件

    • path:文件路径
    • callback:回调函数,没有参数
  • mkdir(path[, options], callback):创建目录

    • path:文件路径

    • options 参数可以是:

      参数 描述
      recursive 是否以递归的方式创建目录,默认为 false
      mode 设置目录权限,默认为 0777
    • callback:回调函数,没有参数

  • readdir(path, callback):读取目录

    • path:文件路径
    • callback:回调函数,有两个参数 err、files,err 为错误信息,files 为目录下的文件数组
    var fs = require("fs");
    fs.readdir("./",function(err, files) {
        if (err) {
          return console.error(err);
        }
        files.forEach( function (file) {
          console.log( file );
        });
    });
  • rmdir(path, callback):删除目录
    • path:文件路径
    • callback:回调函数,没有参数

详细内容请看官方文档:https://nodejs.org/api/fs.html

7、Node.js Stream

Stream 是一个 抽象接口,Node 中很多对象都实现了该接口

(1)Stream 类型

在 Node.js 中,Stream 有四种类型:

  • Readable:可读
  • Writable:可写
  • Duplex:可读可写
  • Transform:操作被写入数据,然后读出结果

(2)Stream 事件

所有的 Stream 对象都是 EventEmitter 的实例,常用的事件有:

  • data:当有数据可读时触发
  • end:当没有数据可读时触发
  • finish:当所有数据都被写入时触发
  • error:在接收和写入过程中发生错误时触发

(3)读取 Stream

var fs = require("fs");
var data = '';
// 创建可读流
var readerStream = fs.createReadStream('input.txt');
// 设置编码
readerStream.setEncoding('UTF8');
// 处理流事件(data、end、error)
readerStream.on('data', function(chunk) {
   data += chunk;
});
readerStream.on('end',function(){
   console.log(data);
});
readerStream.on('error', function(err){
   console.log(err.stack);
});

(4)写入 Stream

var fs = require("fs");
var data = 'Hello World';
// 创建可写流
var writerStream = fs.createWriteStream('output.txt');
// 写入数据
writerStream.write(data,'UTF8');
// 标记文件末尾
writerStream.end();
// 处理流事件(finish、error)
writerStream.on('finish', function() {
    console.log("Finished");
});
writerStream.on('error', function(err){
   console.log(err.stack);
});

(5)管道流与链式流

管道流用于从一个流传递数据到另一个流

var fs = require("fs");
// 创建可读流
var readerStream = fs.createReadStream('input.txt');
// 创建可写流
var writerStream = fs.createWriteStream('output.txt');
// 读取 input.txt 文件内容,并将内容写入 output.txt 文件
readerStream.pipe(writerStream);

链式流一般用于管道,可以理解成多个管道相连

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'));
// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream('input.txt'));

8、Node.js 基础模块

(1)util 模块

util 模块提供 常用函数的集合,常用的方法如下:

  • inherits(constructor, superConstructor):实现对象间原型继承

    JavaScript 面向对象的特性是基于原型的,是通过原型复制实现的

    • constructor:子类构造函数
    • superConstructor:父类构造函数
    var util = require('util');
    
    function Base() { // Base 在构造函数中定义 name 属性、id 属性和 showId 方法
        this.name = 'base';
        this.id = '20190101';
        this.showId = function() {
            console.log(this.id);
        };
    }
    Base.prototype.age = 18; // Base 在原型中定义 age 属性
    Base.prototype.showAge = function() { // Base 在原型中定义 showAge 方法
        console.log(this.age);
    };
    
    function Sub() { // Sub 在构造函数中定义 name 属性
        this.name = 'sub';
    }
    util.inherits(Sub, Base); // Sub 继承 Base
    // Sub 仅仅继承 Base 在原型中定义的 age 属性和 showAge 方法
    // 而不继承 Base 在构造函数中定义的 id 属性和 showId 方法
    // 在原型中定义的属性和方法不会被 console.log 输出
    
    var baseObj = new Base();
    console.log(baseObj.name);
    baseObj.showId();
    baseObj.showAge();
    console.log(baseObj);
    
    var subObj = new Sub();
    console.log(subObj.name);
    // subObj.showId();
    subObj.showAge();
    console.log(subObj);
    
    /* ------ 程序执行结果
      base
      20190101
      18
      Base { name: 'base', id: '20190101', showId: [Function] }
      sub
      18
      Sub { name: 'sub' }
    ------------- */
    
  • inspect(object,[showHidden],[depth],[colors]):将任意对象转换为字符串,通常用于调试和错误输出
    • object:要转换的对象
    • showHidden 若为 true,则输出更多隐藏信息
    • depth:最大递归层数,默认为 2,若为 null,则表示不限递归层数完整遍历对象
    • color 若为 true,则输出格式将会以 ANSI 颜色编码
    var util = require('util');
    function Person() {
        this.name = 'Steve';
        this.showName = function() {
          return this.name;
        };
    }
    var obj = new Person();
    console.log(util.inspect(obj)); 
    console.log(util.inspect(obj, true)); 
    
    /* ------ 程序执行结果
      Person { name: 'Steve', showName: [Function] }
      Person {
        name: 'Steve',
        showName:
        { [Function]
          [length]: 0,
          [name]: '',
          [arguments]: null,
          [caller]: null,
          [prototype]: { [constructor]: [Circular] } } }
    ------------- */

详细内容请看官方文档:https://nodejs.org/api/util.html

(2)os 模块

os 模块提供 基本的系统操作函数,常用的属性和方法如下:

  • EOL:操作系统行尾符的常量
  • type():返回操作系统名
  • platform():返回编译时的操作系统名
  • hostname():返回操作系统主机名
  • cpus():返回一个对象数组,包含每个 CPU 的信息
  • arch():返回 CPU 架构,可能是 "x64"、"arm" 或 "ia32"
  • endianness():返回 CPU 字节序,可能是 "BE" 或 "LE"
  • totalmem():返回操作系统总内存量,单位为字节
  • freemem():返回操作系统空闲内存量,单位为字节
  • networkInterfaces():返回网络接口列表

详细内容请看官方文档:https://nodejs.org/api/os.html

(3)path 模块

path 模块提供 处理文件路径的工具,常用的属性和方法如下:

  • sep:平台的文件路径分隔符,可以是 '\\' 或 '/'
  • delimiter:平台的分隔符,可以是 ';' 或 ':'
  • posix:以 posix 兼容的方式提供 path 方法

  • win32:以 win32 兼容的方式提供 path 方法

  • normalize(path):规范化路径
  • dirname(path):返回路径中的文件夹名称
  • basename(path):返回路径中的文件名称
  • extname(path):返回路径中的文件后缀名
  • parse(pathString):从字符串中返回路径对象
  • format(pathObject):从对象中返回路径字符串
  • isAbsolute(path):判断是否为绝对路径
  • relative(from, to):将绝对路径转为相对路径
  • path.join([path1],[path2] ... [pathN]):连接路径

详细内容请看官方文档:https://nodejs.org/api/path.html

9、Node.js Web

(1)http 模块

http 模块主要用于 搭建 HTTP 服务端和客户端,下面是一个简单的例子:

创建 Web 服务器,在 server.js 文件写入如下代码:

var http = require('http');
var fs = require('fs');
var url = require('url');
// 创建服务器
http.createServer( function (request, response) {
    // 解析请求
    var pathname = url.parse(request.url).pathname;
    // 从文件系统中读取请求的文件
    fs.readFile(pathname.substr(1), function (err, data) {
        if (err) {
            console.log(err);
            // HTTP 状态码: 404 : NOT FOUND
            // Content Type: text/plain
            response.writeHead(404, {'Content-Type': 'text/html'});
        }
        else {
            // HTTP 状态码: 200 : OK
            // Content Type: text/plain
            response.writeHead(200, {'Content-Type': 'text/html'});
            // 响应文件内容
            response.write(data.toString());   
        }
        // 发送响应数据
        response.end();
    });
}).listen(8888);

创建 Web 服务器资源文件,在 index.html 文件写入如下代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello</title>
    </head>
    <body>
        <h1>Hello</h1>
        <p>Welcome to server</p>
    </body>
</html>

输入命令 node server.js 运行 Web 服务端

此时,打开 http://127.0.0.1:8888/index.html,即可看到 index.html 文件内容

创建 Web 客户端,在 client.js 文件写入如下代码:

var http = require('http');

// 用于请求的选项
var options = {
    host: 'localhost',
    port: '8888',
    path: '/index.html'  
};

// 处理响应的回调函数
var callback = function(response){
   var body = '';
   response.on('data', function(data) {
      body += data;
   });
   response.on('end', function() {
      console.log(body);
   });
}

// 向服务端发送请求
var req = http.request(options, callback);
req.end();

输入命令 node client.js 运行 Web 客户端

此时,在控制台在会打印出 index.html 文件内容

详细内容请看官方文档:https://nodejs.org/api/http.html

(2)url 模块

url 模块中的 parse(urlString[,parseQueryString[,slashesDenoteHost]]) 方法可以帮助我们 解析 GET 请求 的内容

  • urlString:要解析的 URL 字符串
  • parseQueryString 如果为 true,则 query 属性将始终被设置为 querystring 模块 parse() 方法返回的对象,默认值为 false
  • slashesDenoteHost 如果为 true,字符串中的 // 和第一个 / 之间的内容将被解释为 host,默认值为 false
// server.js
var http = require('http');
var url = require('url');
http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    // 解析 url 参数
    var params = url.parse(req.url, true).query;
    res.write("Name: " + params.name);
    res.write("\n");
    res.write("URL: " + params.url);
    res.end();
}).listen(8888);

详细内容请看官方文档:https://nodejs.org/api/url.html

(3)querystring 模块

querystring 模块中的 parse(str[,sep[,eq[,options]]]) 方法可以帮助我们 解析 POST 请求 的内容

  • str:要解析的 URL 查询字符串

  • sep:用于在查询字符串中分隔键和值的子字符串,默认值为 '&'

  • eq:用于分隔查询字符串中的键和值的子字符串,默认值为 '='

  • options:对象

    描述
    decodeURIComponent 解码 '%' 字符时使用的函数,默认值为 querystring.unescape()
    maxKeys 要解析的最大键数,指定 0 表示无限制,默认值为 1000
// server.js
var http = require('http');
var querystring = require('querystring');
 
var postHTML = 
  '<html><body>' + 
  '<form method="post">' +
  'Name: <input name="name"><br />' +
  'URL:  <input name="url"><br />' +
  '<input type="submit">' +
  '</form>' +
  '</body></html>';
 
http.createServer(function (req, res) {
    var body = "";
    req.on('data', function (chunk) {
        body += chunk;
    });
    req.on('end', function () {
        body = querystring.parse(body);
        res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});
        if(body.name && body.url) {
            res.write("Name: " + body.name);
            res.write("<br />");
            res.write("URL: " + body.url);
        }
        else {
            res.write(postHTML);
        }
        res.end();
    });
}).listen(8888);

详细内容请看官方文档:https://nodejs.org/api/querystring.html

10、Node.js 全局对象

在 Node.js 中,存在一个 全局对象 global,所有全局变量都是全局对象 global 的属性和方法

注意:尽量使用 var 定义变量,避免引入全局变量污染命名空间

以下介绍常用的全局属性和方法:

  • __filename:当前正在执行的脚本
  • __dirname:当前正在执行的脚本所在的目录
  • setTimeout(cb, ms):按照指定的时间 ms (一次) 来调用函数 cb

    function sayHello(){
       console.log( "Hello, World!");
    }
    // 两秒后执行一次 sayHello 函数
    setTimeout(sayHello, 2000);
  • clearTimeout(t):取消由 setTimeout() 方法设置的 timeout
  • setInterval(cb, ms):按照指定的周期 ms (多次) 来调用函数 cb

    function sayHello(){
       console.log( "Hello, World!");
    }
    // 每隔两秒执行一次 sayHello 函数
    setTimeout(setInterval, 2000);
  • clearInterval(t):取消由 setInterval() 方法设置的 interval
  • console 对象:控制台标准输出
    • console.log(data):标准输出
    • console.info(data):输出提示消息
    • console.error(data):输出错误消息
    • console.warn(data):输出警告消息
    • console.time(label):开始计时
    • console.timeEnd(label):结束计时
    • console.trace(message):输出堆栈信息
  • process 对象:描述当前进程状态
    • exit 事件:当进程退出时触发
    • beforeExit 事件:当清空事件循环时触发
    • uncaughtException 事件:当一个异常冒泡回到事件循环时触发
    • Signal 事件:当进程接收到信号时触发
    • stdout:标准输出流
    • stderr:标准错误流
    • stdin:标准输入流
    • argv:由命令行执行脚本时的各个参数组成的数组,第一个成员总是 node
    • execPath:返回执行路径
    • execArgv:返回执行参数
    • env:返回一个对象,成员为当前 shell 的环境变量
    • exitCode:进程退出时的代码
    • version:Node 的版本
    • versions:Node 的版本和依赖
    • abort():触发 abort 事件
    • exit([code]):使用指定的 code 结束进程
    • kill(pid[, signal]):发送信号 signal 给进程 pid

11、Node.js 多进程

Node.js 以单线程的模式运行,但它使用事件驱动处理并发,这样我们就可以在多核 CPU 的系统上创建 多个子进程,从而提高性能

每个子进程都有三个流对象:child.stdinchild.stdoutchild.stderr,他们可能会共享父进程的 stdio 流,也可能是独立的流对象

child_process 模块 提供创建和管理子进程的工具,常用的方法如下:

  • child_process.exec(command[, options], callback):使用子进程执行命令

    该方法缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回

    • command: 字符串,将要运行的命令,参数使用空格隔开

    • options 参数可以是:

      参数 描述
      cwd 字符串,子进程的当前工作目录
      env 对象,环境变量键值对
      encoding 字符串,字符编码,默认为 'utf8'
      shell 字符串,将要执行命令的 Shell,在 UNIX 中默认为 /bin/sh, 在 Windows 中默认为 cmd.exe
      timeout 数字,超时时间,默认为 0
      maxBuffer 数字, 在 stdout 或 stderr 中允许存在的最大缓冲(二进制),如果超出那么子进程将会被杀死 ,默认为 200*1024
      killSignal 字符串,结束信号,默认为 'SIGTERM'
      uid 数字,设置用户进程的 ID
      gid 数字,设置组进程的 ID
    • callback :回调函数,包含三个参数 error、stdout、stderr

    // support.js
    console.log("exec, process: " + process.argv[2]);
    const fs = require('fs');
    const child_process = require('child_process');
    for(var i=0; i<3; i++) {
        var workerProcess = child_process.exec('node support.js ' + i, function (error, stdout, stderr) {
            if (error) {
                console.log('Stack: ' + error.stack);
                console.log('Code: ' + error.code);
                console.log('Signal: ' + error.signal);
            }
            console.log('stdout: ' + stdout);
        });
        workerProcess.on('exit', function (code) {
            console.log('exit, code: ' + code);
        });
    }
    
    /* ------ 程序执行结果
        exit, code: 0
        stdout: exec, process: 0
    
        exit, code: 0
        stdout: exec, process: 2
    
        exit, code: 0
        stdout: exec, process: 1
    ------------- */
  • child_process.spawn(command[, args][, options]):使用指定命令行参数创建新进程

    该方法返回流 (stdout & stderr),在进程返回大量数据时使用

    • command:字符串,将要运行的命令

    • args:字符串参数数组

    • options 参数可以是:

      参数 描述
      cwd 字符串,子进程的当前工作目录
      env 对象,环境变量键值对
      stdio Array 数组或字符串,子进程的 stdio 配置
      detached 布尔,是否为进程组的领导
      uid 数字,设置用户进程的 ID
      gid 数字,设置组进程的 ID
  • child_process.fork(modulePath[, args][, options]):spawn() 方法的特殊形式

    该方法返回对象,对象拥有 ChildProcess 实例的所有方法,同时拥有一个内建的通信信道

    • modulePath:字符串,将要在子进程中运行的模块

    • args:字符串参数数组

    • options 参数可以是:

      参数 描述
      cwd 字符串,子进程的当前工作目录
      env 对象,环境变量键值对
      execPath 字符串,创建子进程的可执行文件
      execArgv 数组,子进程的可执行文件的字符串参数数组,默认为 process.execArgv
      silent 布尔,若为 true,子进程的 stdin,stdout 和 stderr 会被关联至父进程
      uid 数字,设置用户进程的 ID
      gid 数字,设置组进程的 ID

参考资料:

https://nodejs.org/en/docs/

http://www.runoob.com/nodejs/nodejs-tutorial.html

https://www.liaoxuefeng.com

猜你喜欢

转载自www.cnblogs.com/wsmrzx/p/10427420.html