Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node-常用API #36

Open
lq782655835 opened this issue Dec 7, 2018 · 0 comments
Open

Node-常用API #36

lq782655835 opened this issue Dec 7, 2018 · 0 comments

Comments

@lq782655835
Copy link
Owner

总结NodeJS常用的API,重点地方有笔者的释义以及详细说明。关联度高的模块放一起叙述,并有对比说明,比如buffer与fs,stream与http,process与child_process。本文尽量做到兼具实用与API广度,更多详细内容请看Node.JS官网文档

path

  • __filename。全局值,当前文件绝对路径
  • __dirname。全局值,当前文件夹绝对路径。等效于path.resolve(__filename, '..')
  • path.join([...paths])。相当于把所传入的任意多的参数 按照顺序 进行命令行般的推进
    path.join('a','b','../c/lolo') // a/c/lolo
  • path.resolve([...paths])。以当前文件的路径为起点,返回绝对路径。可以理解为每次都是新建cd命令
    path.resolve('/a', '/b') // '/b'
    path.resolve('./a', './b') // '/User/../a/b'
  • path.dirname(path(string))。返回指定的路径 所在文件夹的绝对路径
  • path.basename(path(string))。返回指定Path路径所在文件的名字
  • path.extname(path | string)。获取指定字符串或者文件路径名字的后缀名,带.比如.txt

url

url 模块提供了两套 API 来处理 URL 字符串:一个是Node.js特有的API,是旧版本的遗留;另一个则是实现了WHATWG URL Standard的 API (const {URL} = require('url')方式),该标准也在各种浏览器中被使用。

旧版url api,新版URL Standard见这

  • url.parse(urlString[, parseQueryString[, slashesDenoteHost]])。把url字符串解析为url对象
  • url.format(urlObject)。把url对象解析为字符串
  • url.resolve(from, to)。以一种 Web 浏览器解析超链接的方式, 基于一个基础 URL,对目标 URL进行解析。查看其源码实现:
    Url.prototype.resolve = function(relative) {
    return this.resolveObject(urlParse(relative, false, true)).format();
    };

querystring

  • querystring.parse。一个URL查询字符串 str 解析成一个键值对的集合。
  • querystring.stringify。遍历给定的 obj 对象的自身属性,生成 URL 查询字符串。

Stream

Node中很多数据结构都继承自Stream。Stream在文件系统和http请求中,有非常大的作用。

  • 对于文件处理,小文件对于buffer数据结构能很好的解决,直接将文件所有数据加载到内存中;但对于大文件,buffer这种方式不可取,容易打爆内存。最好的方式是像流水一样,将读取的数据实时流入到写入的文件,直到数据全部读完并写入好对应目标文件。举个例子,就像两个水池,待读取的文件是一个装满水的池子,写入的空文件是一个等待蓄满水的空池子。通过Stream API的方式,将两个池子嫁接起来,使得满水的池子可以持续注水到空池子中。而且Stream继承自EmitEvents,过程中会有许多回调事件发出,供开发人员自定义内容。
  • 对于http请求处理,最常见的就是通过流去写入内容。

以下对象继承了可读流:

  • HTTP responses, on the client
  • HTTP requests, on the server
  • fs read streams
  • child process stdout and stderr
  • process.stdin
  • zlib streams
  • crypto streams
  • TCP sockets

Stream类

  • stream.Writable
    • write() 写入数据到流
    • end() 表明已没有数据要被写入
    • Events
      • drain。 每个数据块写入成功后回调事件,可能多次触发。
      • pipe。当readableStream.pipe()调用时回调事件
      • finish。 当writeableStream.end()调用并数据读完后回调事件。
      • close
      • error
  • stream.Readable
    • pipe(destination:stream.Writable, options) 把可读数据流入可写流中
    • pause() 流暂停。使读取流暂停emit 'data'事件
    • resume() 流重启。重新开始emit 'data'事件
    • unpipe()
    • Events
      • data。每次读取完一段数据块回调事件,可能多次触发。
      • readable。数据读取可用时回调事件。
      • end。 数据全部读取完成回调事件。
      • close
      • error
  • stream.Duplex
  • stream.Transform

以下举一个完整例子:

const fs = require('fs')

let readStream = fs.createReadStream('./node/snapshot/test1.png')
let writeStream = fs.createWriteStream('./node/snapshot/test1-copy.png')

readStream
    .on('data', (chunk) => {
        // 当读取较大文件时,写入流的速度可能没有读入的速度快
        // 所以这里做了一个处理,没写完暂停读入流
        if (writeStream.write(chunk) === false)
            readStream.pause()
    })
    .on('end', () => writeStream.end())

// 这里配合上面pause,写完了继续读入流
writeStream.on('drain', () => readStream.resume())

// 以上换成管道的方式,只需一行代码:readStream.pipe(writeStream)

http

  • http.Server。http.createServer(function(req, res){})返回该类。
    • listen()
  • http.ClientRequestNode作为客户端。http.get()/http.request()返回该类。
    • 可写流。详细参见上章节stream.Writable
    • write(chunk[, encoding][, callback])。stream继承,请求写入数据,一般是POST请求需要。
    • end([data][, encoding][, callback])。stream继承,请求发出。
    • 回调函数res是可读流
  • http.ServerResponseNode作为服务端。服务端res即是该类的实例。
    • 可写流
    • write(chunk[, encoding][, callback])
    • end([data][, encoding][, callback])
    • setHeader(name, value)
    • writeHead(statusCode[, statusMessage][, headers])
// Node作为客户端发送请求
const postData = querystring.stringify({
  'msg': 'Hello World!'
});
const options = {
  hostname: 'www.google.com',
  port: 80,
  path: '/upload',
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': Buffer.byteLength(postData)
  }
};

const req = http.request(options, (res) => {
  // 响应值res作为从目标接口中获取到的值,是个readable.Stream
  res.setEncoding('utf8');
  res.on('data', (chunk) => console.log(`BODY: ${chunk}`));
  res.on('end', () => console.log('No more data in response.'));
});
req.on('error', (e) => {
  console.error(`problem with request: ${e.message}`);
});

// req作为客户端请求,是个writeable.Stream。添加上参数并请求出去
req.write(postData);
req.end();
// Node作为服务端,res(response)参数继承的是writeable.Stream
const http = require('http')

http
    .createServer((req, res) => {
        res.write('hello world')
        res.end()
    })
    .listen(3001)

http.get()与 http.request() 唯一的区别是它设置请求方法为 GET 且自动调用 req.end()

在node是客户端的时候,req是从node这边发起的,是可写流,res是从外边响应的,是可读流。

在node是服务端的时候,req是从客户端发起的,是可读流,res是从node响应的,是可写流。

Bufferglobal

在 TCP 流或文件系统操作等场景中处理字节流。Buffer 类是一个全局变量。Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。 Buffer 的大小在创建时确定,且无法改变。

Node.js v6.0.0 之前,Buffer 实例是通过 Buffer 构造函数创建的,它根据参数返回不同的 Buffer。为了使 Buffer 实例的创建更可靠,new Buffer() 构造函数已被废弃,建议使用 Buffer.from()、Buffer.alloc()和 Buffer.allocUnsafe()

  • 创建Buffer
    • new Buffer(number/string/array)Deprecated。推荐使用 Buffer.from(array/string) 和Buffer.alloc(size)代替。
    • Buffer.from(array/string)
    • Buffer.alloc(size)
  • Buffer静态方法
    • Buffer.isBuffer(obj)
    • Buffer.byteLength(string)
    • Buffer.concat()
    • Buffer.compare()
  • Buffer实例
    • length
    • toString() buffer转为string
    • write(string, offset=0, length, encoding='utf8')。对buffer对象写入string
    • copy()
    • slice()
    • compare()
    • equals()
    • fill()
let buf = new Buffer('hello world') // 初始化之后,实例buf长度无法改变
console.log(buf.length, buf.toString()) // 11, hello world

buf.write('temp')
console.log(buf.length, buf.toString()) // 11, tempo world

buf.write('01234567891234567890')
console.log(buf.length, buf.toString()) // 11, 01234567891

File System

  • file文件操作
    • readFile(path[, options], callback)

      • 没有指定 encoding,则返回原始的 buffer
    • writeFile(file, data[, options], callback)

      • 如果文件已存在,则覆盖文件。
      • data支持 string/Buffer/TypedArray/DataView
      • data 是一个 buffer,则忽略 encoding
    • copyFile(src, dest[, flags], callback)

    • rename(oldPath, newPath, callback)。文件重命名

    • write(fd, string[, positinon[, encoding]], callback)。将 string 写入到 fd 指定的文件写文件

    • exists(url, callback(boolean))Deprecated。查询是否存在,一般用在单纯检查文件而不去操作(open/readFile/writeFile等操作文件不成立时会回调err)文件时。推荐使用fs.stat() or fs.access() 代替该方法。

    • stat(path[, options],callback(err, stat))。查询文件/目录信息

      • stat.isFile 是否一个普通文件
      • stat.isDirectory 是否一个目录
      • stat.ctime 最后一次修改文件的时间
    • createReadStream(path[, options])。 指定路径读取,获得Readable Stream。

    • createWriteStream(path[, options])。创建空的写入流到目标路径,获得Writeable Stream。

  • dir目录操作
    • readdir(path[, options], callback)。读目录,获取目录下的所有文件和文件夹名称。
    • rmdir(path, callback)。移除目录

文件操作的path参数,绝对路径和相对路径都支持(相对路径基于process.cwd())。

const fs = require('fs')
const path = require('path')
let dir = './node/snapshot'
fs.readFile(path.join(dir, 'test1.png'), (err, data) => {
    console.log(Buffer.isBuffer(data)) // true

    fs.writeFile(path.join(dir, 'test1_copy.png'), data, (error) => console.log(error)
})

Processglobal

process对象是一个提供当前node进程信息的全局对象,所以该对象不需要require()引入。process同时也是EventEmitter(典型的发布订阅模式案例)的一个实例,所以有.on()等方法。

  • process.argv。一个包含命令行参数的数组。第一个元素是’node’,第二个元素是JavaScript文件的文件名。接下来的元素则是附加的命令行参数。

    // process.js
    process.argv.forEach(function(val, index, array) {
    console.log(index + ': ' + val);
    });
    
    // output
    $ node process.js one two=three four
    0: node
    1: /Users/node/process.js
    2: one
    3: two=three
    4: four
  • process.env。返回用户设置的环境变量。

    // index.js
    console.log(process.env.NODE_ENV) // production
    
    // output
    $ cross-env NODE_ENV=production node index
    production
  • process.cwd()。返回当前进程的工作目录

    和 __dirname 不同, __dirname 返回的是当前文件的所在目录

  • process.exit()。退出当前程序。

    当执行exit()方法时,可以使用process.on('exit', callback)作为退出程序前的清理工作。

  • process signal Events。当标准POSIX信号被触发(通常是process.kill(signal)或Ctrl+C等操作),nodejs进程可以通过监听对应信号来进行回调。

    • SIGINT:interrupt,程序终止信号,通常在用户按下CTRL+C时发出,用来通知前台进程终止进程。
    • SIGTERM:terminate,程序结束信号,通常用来要求程序自己正常退出。process.kill()缺省产生这个信号。

child_process

子程序,在node中,child_process这个模块非常重要。熟悉shell脚本的同学,可以用它的异步特性完成很多事情。

异步创建子程序有四种方式,后三种底层都是spawn实现的:

  • child_process.spawn(command[, args][, options])
  • child_process.exec(command[, options][, callback])
  • child_process.execFile(file[, args][, options][, callback])
  • child_process.fork(modulePath[, args][, options])

create child_process

  • child_process.spawn。Node.js 的父进程与衍生的子进程之间会建立 stdin、stdout 和 stderr 的管道。
    • options.stdio: stdio(标准输入输出) 用来配置子进程和父进程之间的 IO 通道,可以传递一个数组或者字符串。如,['pipe', 'pipe', 'pipe'],分别配置:标准输入、标准输出、标准错误。如果传递字符串,则三者将被配置成一样的值。简要介绍其中三个可以取的值:
      • pipe(默认):父子进程间建立 pipe 通道,可以通过 stream 的方式来操作 IO
      • inherit:子进程直接使用父进程的 IO(该种情况使用较多,子进程命令中,执行的node文件里使用process对象与主文件中一致)
      • ignore:不建立 pipe 通道,不能 pipe、不能监听 data 事件、IO 全被忽略
const { spawn } = require('child_process');
var ls = spawn('ls', ['-al'],{
    stdio: 'inherit'
});

ls.stdout.on('data', function(data){
    console.log('data from child: ' + data);
});


ls.stderr.on('data', function(data){
    console.log('error from child: ' + data);
});

ls.on('close', function(code){
    console.log('child exists with code: ' + code);
});
  • child_process.exec。创建一个shell,然后在shell里执行命令。执行完成后,将stdout、stderr作为参数传入回调方法。exec 比较适合用来执行 shell 命令,然后获取输出。
const { exec } = require('child_process');

exec('ls -al', function(error, stdout, stderr){
    if(error) {
        console.error('error: ' + error);
        return;
    }
    console.log('stdout: ' + stdout);
    console.log('stderr: ' + typeof stderr);
});
  • events。child_process支持以下事件:
    • exit。子进程退出。注意其和close的区别,当exit触发时,其stdio流有可能还打开着,可以在此时做一些清理工作。通常情况下,child_process.kill()会触发该事件。
    • close。当子进程关闭时。通常情况下,child_process.kill()也会触发该事件。
    • error。当子进程不能关闭时,关闭它会报error事件。调用kill()可能会触发该事件。
    • message。跟child_process.send方法有关,父子进程间通信。
    • disconnect。跟child_process.disconnect方法有关。
var child_process = require('child_process')

var proc = child_process.spawn('pm2-runtime', ['proxy-server', '--', './dist'], { stdio: 'inherit' })

process.on('SIGTERM', () => proc.kill('SIGTERM'))
process.on('SIGINT', () => proc.kill('SIGINT'))
process.on('SIGBREAK', () => proc.kill('SIGBREAK'))
process.on('SIGHUP', () => proc.kill('SIGHUP'))

proc.on('exit', process.exit)

参考文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant