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.stream 流式渲染 #6

Open
Hazlank opened this issue Aug 29, 2021 · 0 comments
Open

Node.stream 流式渲染 #6

Hazlank opened this issue Aug 29, 2021 · 0 comments

Comments

@Hazlank
Copy link
Owner

Hazlank commented Aug 29, 2021

前言

React16增加了很多新的内容,比如使用Fiber重写了核心的算法,ssr增加了renderToStream,render支持返回数组和字符串等等。
因为对于ssr了解的不够多,所以主要来了解renderToStream的主要核心。

Node流

不管是vue还是react的renderToStream,他们都是返回Node.stream,那么啥是流?
我们可以想象有一个很大的水箱,当打开水龙头的时候,水会经过水龙头的限制决定它流水量。在stream里,限制流水量大小的就是buffer。它是一个内部缓冲区,它的size决定了最多能往里面读多少字节。当水流走的时候又可以有新的水进来,直到流完整个水箱。

流的读取是异步的,它可以减少发出页面请求到接收到应答数据第一个字节所花费的毫秒数(TTFB),当读取一个大文件时,不需要等待所有的数据读完再呈现给用户,并且不需要把整个文件缓冲到node内存里占据程序空间,这对于性能和用户感知体验都是很大的提升。

Node里有很多内置模块都可以支持可读写的stream
streams

stream类型

stream有四种类型

  • Readable streams:可读 stream
  • Writable streams:可写 stream
  • Duplex streams:可读写(全双工) stream
  • Transform streams:也类似于Duplex stream
    主要用到的还是Readable streams和Writable streams,下面是他们的主要方法:
    streams

Readable streams

Readable Stream可以读取很多数据,比如读取本地文件、服务器数据。
我们创建一个Readable Stream,通过这个Readable Stream来读取本都文件的数据。

const fs = require('fs');
const server = require('http').createServer();

server.on('request', (req, res) => {
  const stream = fs.createReadStream('./big.file');
  stream.on('data', chunk=> {
   	res.write(chunk)
  })
  
  stream.on('end', () => {
    console.log('finish') 
  })
  
  stream.on('error', err => {
    // handle error...
  })
});

server.listen(3001);

我们创建一个fileReadStream,流是异步的,它基于EventEmitter类的接口。EventEmitter 本身是一个非常简单的类,许多其他实体使用或继承它。它负责侦听和发出事件,方法如 .on() 和 .emit()。

当监听有数据进来的时候,将buffer转成string输送到response。也可以使用 stream.pipe()来完成自动读写到目标。它可以在两个流建立管道,让buffer自动流向其他流

const fs = require('fs');
const server = require('http').createServer();

server.on('request', (req, res) => {
  const stream = fs.createReadStream('./big.file');
  stream.pipe(res);
});

server.listen(3001);

当然,stream内置的buffer是有设置固定的最大值readable.readableHighWaterMark //65536~64kb,当然也可以设置到最小来看看流式渲染的过程

const stream = fs.createReadStream('./big.file', {highWaterMark: 2});

streams

整个过程还是挺流畅的,只不过是我战五渣i5,8g内存电脑录制卡而已

stream的好处还有很多,它能够暂停和继续流,当服务器压力大时,可以停止流的运行直到不再网络阻塞。

  • stream.pause()会停止流的读取
  • stream.resume()会继续读取流数据
    streams

Writeable streams

有读流,当然也会有写流

const fs = require('fs');
const file = fs.createWriteStream('example.txt');
file.write('hello, ');
file.end('world!');

write方法返回一个布尔值,指示内部缓冲区中是否还有一些空间。如果它已满(返回值为 false),就应该停止写入数据并等待drain事件,重新开始写入。不然可能会导致内存使用率过高、错误,从而导致崩溃。

writable.on("drain", () => {
    console.log("You can continue the writing process!");
});

Writeable还有很多其他事件pipe/unpipe,finish,error,close,当然也有很多函数。

在提供小块数据时,过多调用 .write() 方法可能会导致性能下降。对于此类场景,可写流提供 .cork().uncork() 方法。调用 .cork() 方法后,所有使用 .write() 写入的数据都将保存到内存而不是缓冲区。通过这种方式,可以轻松地对较小的数据块进行批处理以提高性能。可以在之后使用 .uncork() 方法将数据从内存推送到缓冲区。这些方法以类似于后进先出(LIFO)的顺序线性工作。 .uncork() 调用的次数需要与 .cork() 方法相同。

它还能够设置整个流的编码

writable.setDefaultEncoding("utf8");
@Hazlank Hazlank changed the title Node(stream) Node.stream 流式渲染 Aug 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant